Una descripción general fundamental de la creación de un componente de pestañas similar a los de las apps para iOS y Android.
En esta publicación, quiero compartir las ideas sobre cómo crear un componente de pestañas para la Web que sea responsivo, sea compatible con varias entradas de dispositivos y funcione en todos los navegadores. Prueba la demostración.
Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:
Descripción general
Las pestañas son un componente común de los sistemas de diseño, pero pueden adoptar muchas formas. Primero, había pestañas de computadora de escritorio compiladas en el elemento <frame>
y ahora tenemos componentes móviles de mantequilla que animan contenido según las propiedades físicas.
Todos intentan hacer lo mismo: ahorrar espacio.
Hoy en día, los aspectos esenciales de la experiencia del usuario con pestañas son un área de navegación con botones que activa o desactiva la visibilidad del contenido en un marco de visualización. Muchas áreas de contenido diferentes comparten el mismo espacio, pero se presentan condicionalmente en función del botón seleccionado en la navegación.
![el collage es bastante caótico debido a la enorme diversidad de estilos que la Web aplicó al concepto de componentes](https://web.dev/static/articles/building/a-tabs-component/image/the-collage-is-quite-chao-b6f8cd38f8ddd.png?authuser=6&hl=es)
Tácticas web
En general, este componente me pareció bastante fácil de crear, gracias a algunas funciones fundamentales de la plataforma web:
scroll-snap-points
para una interacción elegante con el deslizamiento y el teclado con las posiciones de desplazamiento adecuadas- Vínculos directos a través de hashes de URL para el anclaje de desplazamiento en la página y la compatibilidad con el uso compartido del navegador
- Compatibilidad con lectores de pantalla con lenguaje de marcado de elementos
<a>
yid="#hash"
prefers-reduced-motion
para habilitar las transiciones de encadenado y el desplazamiento instantáneo en la página- La función web
@scroll-timeline
en borrador para subrayar y cambiar de color dinámicamente la pestaña seleccionada
El código HTML
En esencia, la UX es la siguiente: hacer clic en un vínculo, hacer que la URL represente el estado de la página anidada y, luego, ver la actualización del área de contenido a medida que el navegador se desplaza hasta el elemento coincidente.
Hay algunos miembros de contenido estructural allí: vínculos y :target
. Necesitamos una lista de vínculos, para qué sirve un <nav>
, y una lista de elementos <article>
, para los que es ideal un elemento <section>
. Cada hash de vínculo coincidirá con una sección,
lo que permitirá que el navegador desplace las cosas mediante el anclaje.
Por ejemplo, si haces clic en un vínculo, se enfoca automáticamente el artículo :target
en Chrome 89, no se requiere código JS. Luego, el usuario puede desplazar el contenido del artículo con su dispositivo de entrada como siempre. Es contenido gratuito, como se indica en el lenguaje de marcado.
Usé el siguiente lenguaje de marcado para organizar las pestañas:
<snap-tabs>
<header>
<nav>
<a></a>
<a></a>
<a></a>
<a></a>
</nav>
</header>
<section>
<article></article>
<article></article>
<article></article>
<article></article>
</section>
</snap-tabs>
Puedo establecer conexiones entre los elementos <a>
y <article>
con las propiedades href
y id
de la siguiente manera:
<snap-tabs>
<header>
<nav>
<a href="#responsive"></a>
<a href="#accessible"></a>
<a href="#overscroll"></a>
<a href="#more"></a>
</nav>
</header>
<section>
<article id="responsive"></article>
<article id="accessible"></article>
<article id="overscroll"></article>
<article id="more"></article>
</section>
</snap-tabs>
Luego, llené los artículos con una mezcla de lorem, y los enlaces con una longitud mixta y un conjunto de imágenes de títulos. Con contenido para trabajar, podemos iniciar el diseño.
Diseños con desplazamiento
Hay 3 tipos diferentes de áreas de desplazamiento en este componente:
- La barra de navegación (rosa) se puede desplazar horizontalmente.
- El área de contenido (azul) se puede desplazar horizontalmente
- Cada elemento del artículo (verde) se puede desplazar verticalmente.
![3 cuadros de colores con flechas de dirección de colores que delinean las áreas de desplazamiento y muestran la dirección en la que lo harán.](https://web.dev/static/articles/building/a-tabs-component/image/3-colorful-boxes-color-m-da0de23b13551.png?authuser=6&hl=es)
Hay 2 tipos diferentes de elementos involucrados en el desplazamiento:
- Una ventana
Es un cuadro con dimensiones definidas que tiene el estilo de propiedadoverflow
. - Una superficie de gran tamaño
En este diseño, son los contenedores de lista: vínculos de navegación, artículos de secciones y contenidos de artículos.
Diseño <snap-tabs>
El diseño de nivel superior que elegí fue flex (Flexbox). Configuré la dirección en column
para que el encabezado y la sección se ordenen verticalmente. Esta es nuestra primera ventana de desplazamiento y oculta todo con el desbordamiento oculto. El encabezado y la sección pronto usarán el sobredesplazamiento, como zonas individuales.
<snap-tabs> <header></header> <section></section> </snap-tabs>
snap-tabs { display: flex; flex-direction: column; /* establish primary containing box */ overflow: hidden; position: relative; & > section { /* be pushy about consuming all space */ block-size: 100%; } & > header { /* defend againstneeding 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }
Señalamos el colorido diagrama de 3 desplazamientos:
<header>
ahora está preparado para ser el contenedor de desplazamiento (rosa).<section>
está preparado para ser el contenedor de desplazamiento (azul).
Los marcos que se destacan a continuación con VisBug nos ayudan a ver las ventanas que crearon los contenedores de desplazamiento.
![Los elementos del encabezado y la sección tienen superposiciones de color rosca y delinean el espacio que ocupan en el componente.](https://web.dev/static/articles/building/a-tabs-component/image/the-header-section-eleme-dd313d7eb1453.png?authuser=6&hl=es)
Diseño de las pestañas <header>
El siguiente diseño es casi el mismo: uso Flex para crear un orden vertical.
<snap-tabs> <header> <nav></nav> <span class="snap-indicator"></span> </header> <section></section> </snap-tabs>
header { display: flex; flex-direction: column; }
El .snap-indicator
debe viajar horizontalmente con el grupo de vínculos, y este diseño de encabezado ayuda a establecer esa etapa. Aquí no hay elementos posicionados absolutos.
![los elementos nav y span.indicator tienen superposiciones de color hojuelas en las que se destaca el espacio que ocupan en el componente.](https://web.dev/static/articles/building/a-tabs-component/image/the-nav-spanindicator-e-eddaf5b04df39.png?authuser=6&hl=es)
A continuación, los estilos de desplazamiento. Resulta que podemos compartir los estilos de desplazamiento entre nuestras 2 áreas de desplazamiento horizontal (encabezado y sección), por lo que creé una clase de utilidad, .scroll-snap-x
.
.scroll-snap-x {
/* browser decide if x is ok to scroll and show bars on, y hidden */
overflow: auto hidden;
/* prevent scroll chaining on x scroll */
overscroll-behavior-x: contain;
/* scrolling should snap children on x */
scroll-snap-type: x mandatory;
@media (hover: none) {
scrollbar-width: none;
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
}
Cada uno necesita un desbordamiento en el eje x, contención de desplazamiento para capturar el sobredesplazamiento, barras de desplazamiento ocultas para dispositivos táctiles y, por último, ajuste de desplazamiento para bloquear áreas de presentación de contenido. Nuestro orden de tabulación del teclado es accesible y cualquier guía de interacción se enfoca de forma natural. Los contenedores de ajuste de desplazamiento también obtienen una buena interacción de estilo carrusel desde el teclado.
Diseño del encabezado de las pestañas <nav>
Los vínculos de navegación deben presentarse en una línea, sin saltos de línea, centrados verticalmente, y cada elemento de vínculo debe ajustarse al contenedor de ajuste de desplazamiento. Swift trabaja para las CSS de 2021.
<nav> <a></a> <a></a> <a></a> <a></a> </nav>
nav { display: flex; & a { scroll-snap-align: start; display: inline-flex; align-items: center; white-space: nowrap; } }
Los estilos y tamaños de cada vínculo, por lo que el diseño de navegación solo necesita especificar la dirección y el flujo. Los anchos únicos en los elementos de navegación hacen que la transición entre pestañas sea divertida, ya que el indicador ajusta su ancho según el nuevo destino. Según la cantidad de elementos que haya aquí, el navegador renderizará o no una barra de desplazamiento.
![Los elementos a de la navegación tienen superposiciones de color rosa, que destacan el espacio que ocupan en el componente, así como dónde se desbordan.](https://web.dev/static/articles/building/a-tabs-component/image/the-elements-the-nav-h-c8efe2b10b412.png?authuser=6&hl=es)
Diseño de las pestañas <section>
Esta sección es un elemento flexible y debe ser el consumidor dominante de espacio. También debe crear columnas para colocar los artículos. Una vez más, ¡rápido
para CSS 2021! block-size: 100%
estira este elemento para llenar el elemento superior tanto como sea posible y, luego, para su propio diseño, crea una serie de columnas que tienen 100%
el ancho del elemento superior. Los porcentajes funcionan muy bien aquí
porque escribimos restricciones sólidas sobre el elemento superior.
<section> <article></article> <article></article> <article></article> <article></article> </section>
section { block-size: 100%; display: grid; grid-auto-flow: column; grid-auto-columns: 100%; }
Es como si dijera "expandir verticalmente tanto como sea posible, de manera insistente" (recuerda el encabezado que establecimos en flex-shrink: 0
: es una defensa contra este envío de expansión), que establece la altura de fila para un conjunto de columnas de altura completa. El estilo auto-flow
le indica a la cuadrícula que siempre coloque los elementos secundarios en una línea horizontal, sin unión, exactamente lo que queremos; para desbordar la ventana superior.
![los elementos del artículo tienen superposiciones de color rosa, que destacan el espacio que ocupan en el componente y dónde se desbordan](https://web.dev/static/articles/building/a-tabs-component/image/the-article-elements-hot-924ae524ee8fb.png?authuser=6&hl=es)
A veces, esto me resulta difícil de entender. Este elemento de la sección encaja en un cuadro, pero también crea un conjunto de cuadros. Espero que las imágenes y las explicaciones te ayuden.
Diseño de las pestañas <article>
El usuario debería poder desplazar el contenido del artículo, y las barras de desplazamiento solo deberían aparecer si hay un desbordamiento. Estos elementos del artículo están en una posición prolija. Son simultáneamente un elemento superior de desplazamiento y un elemento secundario de desplazamiento. En este caso, el navegador está manejando algunas interacciones complicadas con el tacto, el mouse y el teclado.
<article> <h2></h2> <p></p> <p></p> <h2></h2> <p></p> <p></p> ... </article>
article { scroll-snap-align: start; overflow-y: auto; overscroll-behavior-y: contain; }
Elegí que los artículos se ajustaran dentro de su barra de desplazamiento superior. Me gusta mucho cómo los elementos de los vínculos de navegación y del artículo se ajustan al inicio intercalado de sus respectivos contenedores de desplazamiento. Se ve y se siente como una relación armoniosa.
![el elemento del artículo y sus elementos secundarios tienen superposiciones de color naranja que describen el espacio que ocupan en el componente y la dirección en la que se desbordan](https://web.dev/static/articles/building/a-tabs-component/image/the-article-element-its-8afcb8a6817ff.png?authuser=6&hl=es)
El artículo es un elemento secundario de la cuadrícula y su tamaño está predeterminado para ser el área del viewport con la que queremos proporcionar una UX de desplazamiento. Esto significa que no necesito ningún estilo de altura o ancho, solo necesito definir cómo se desborda. Configuré overflow-y como automático y, luego, atrapamos las interacciones de desplazamiento con la práctica propiedad de comportamiento de sobredesplazamiento.
Resumen de las 3 áreas de desplazamiento
A continuación, elegí la opción "mostrar siempre las barras de desplazamiento" en la configuración del sistema. Creo que es doblemente importante que el diseño funcione con esta configuración activada, ya que lo es para mí revisar el diseño y la organización de desplazamiento.
![Las 3 barras de desplazamiento están configuradas para mostrarse, lo que ahora consume espacio de diseño, y nuestro componente se ve muy bien.](https://web.dev/static/articles/building/a-tabs-component/image/the-3-scrollbars-are-set-ca02ac51f046d.png?authuser=6&hl=es)
Creo que ver el margen de la barra de desplazamiento en este componente ayuda a mostrar claramente dónde están las áreas de desplazamiento, la dirección que admiten y cómo interactúan entre sí. Considera cómo cada uno de estos marcos de ventana de desplazamiento también son flexibles o superiores de cuadrícula para un diseño.
Las Herramientas para desarrolladores pueden ayudarnos a visualizar esto:
![las áreas de desplazamiento tienen superposiciones de herramientas de cuadrícula y flexbox, que delinean el espacio que ocupan en el componente y la dirección en la que se desbordan.](https://web.dev/static/articles/building/a-tabs-component/image/the-scroll-areas-grid-f-79f0ae5839bcc.png?authuser=6&hl=es)
Los diseños de desplazamiento están completos: ajuste, vinculación directa y acceso con teclado. Base sólida para las mejoras, el estilo y el deleite de la UX.
Destacado de la función
Los elementos secundarios ajustados por desplazamiento mantienen su posición bloqueada durante el cambio de tamaño. Esto significa que JavaScript no necesitará mostrar nada cuando se rote el dispositivo o cambie el tamaño del navegador. Pruébalo en el Modo de dispositivo de las Herramientas para desarrolladores de Chromium. Para ello, selecciona cualquier modo que no sea Responsivo y, luego, cambia el tamaño del marco del dispositivo. Observa que el elemento permanece a la vista y bloqueado con su contenido. Esta función está disponible desde que Chromium actualizó su implementación para que coincida con la especificación. Aquí encontrarás una entrada de blog al respecto.
Animación
El objetivo del trabajo de animación aquí es vincular claramente las interacciones con los comentarios de la IU. Esto ayuda a guiar o asistir al usuario para que descubra (con suerte) todo el contenido sin problemas. Agregaré movimiento con propósito y condicionalmente. Ahora los usuarios pueden especificar sus preferencias de movimiento en sus sistemas operativos, y yo disfruto mucho responder a sus preferencias en mis interfaces.
Vincularé una pestaña subrayada con la posición de desplazamiento del artículo. El ajuste no es solo una alineación bonita, sino que también ancla el inicio y el final de una animación.
Esto mantiene el <nav>
, que actúa como un minimapa, conectado al contenido.
Verificaremos la preferencia de movimiento del usuario tanto desde CSS como JS. ¡Hay varios lugares
excelentes para ser considerado!
Comportamiento de desplazamiento
Existe la oportunidad de mejorar el comportamiento de movimiento de :target
y element.scrollIntoView()
. De forma predeterminada, es instantáneo. El navegador solo establece la posición de desplazamiento. ¿Qué sucede si queremos hacer la transición a esa posición de desplazamiento, en lugar de parpadear allí?
@media (prefers-reduced-motion: no-preference) {
.scroll-snap-x {
scroll-behavior: smooth;
}
}
Dado que introducimos movimiento aquí y movimientos que el usuario no controla (como el desplazamiento), solo aplicamos este estilo si el usuario no prefiere el movimiento reducido en su sistema operativo. De esta manera, solo presentamos el movimiento de desplazamiento para las personas que están de acuerdo.
Indicador de pestañas
El propósito de esta animación es ayudar a asociar el indicador con el estado del contenido. Decidí cambiar el color de los estilos de border-bottom
para los usuarios que prefieren un movimiento reducido y una animación de deslizamiento y atenuación de color con desplazamiento para los usuarios que no aceptan movimiento.
En Chromium Devtools, puedo activar o desactivar la preferencia y mostrar los 2 estilos de transición diferentes. Me divertí muchísimo creando esto.
@media (prefers-reduced-motion: reduce) {
snap-tabs > header a {
border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
transition: color .7s ease, border-color .5s ease;
&:is(:target,:active,[active]) {
color: var(--text-active-color);
border-block-end-color: hsl(var(--accent));
}
}
snap-tabs .snap-indicator {
visibility: hidden;
}
}
Oculto el objeto .snap-indicator
cuando el usuario prefiere un movimiento reducido, ya que ya no lo necesito. Luego, lo reemplazo por diseños de border-block-end
y transition
. También observa en la interacción de las pestañas que el elemento de navegación activo no solo tiene un subrayado de marca destacado, sino que el color del texto también es más oscuro. El elemento activo tiene un mayor contraste de color del texto y un contraste brillante con poca luz.
Solo algunas líneas adicionales de CSS harán que alguien se sienta valorado (en el sentido de que respetamos cuidadosamente sus preferencias de movimiento). Me encanta.
@scroll-timeline
En la sección anterior, te mostré cómo controlo los estilos de encadenado con movimiento reducido y, en esta sección, te mostraré cómo vinculé el indicador y un área de desplazamiento al mismo tiempo. A continuación, veremos más experimentos divertidos. Espero que estés tan emocionado como yo.
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
);
Primero, reviso la preferencia de movimiento del usuario desde JavaScript. Si el resultado de esto es false
, lo que significa que el usuario prefiere un movimiento reducido, no ejecutaremos ninguno de los efectos de movimiento de vinculación de desplazamiento.
if (motionOK) {
// motion based animation code
}
Al momento de escribir esto, la compatibilidad del navegador con @scroll-timeline
no es compatible. Es una especificación en borrador que solo incluye implementaciones experimentales. Pero sí tiene un polyfill, que uso en esta demostración.
ScrollTimeline
Aunque CSS y JavaScript pueden crear cronogramas de desplazamiento, habilité JavaScript para poder usar las mediciones de elementos activos en la animación.
const sectionScrollTimeline = new ScrollTimeline({
scrollSource: tabsection, // snap-tabs > section
orientation: 'inline', // scroll in the direction letters flow
fill: 'both', // bi-directional linking
});
Quiero que 1 cosa siga la posición de desplazamiento de otra y, cuando creo un ScrollTimeline
, defino el controlador del vínculo de desplazamiento, el scrollSource
.
Por lo general, una animación en la Web se ejecuta en una marca de tiempo global, pero, con un sectionScrollTimeline
personalizado en la memoria, puedo cambiar todo eso.
tabindicator.animate({
transform: ...,
width: ...,
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
Antes de entrar en los fotogramas clave de la animación, creo que es importante señalar que el seguidor del desplazamiento, tabindicator
, se animará en función de un cronograma personalizado, el desplazamiento de nuestra sección. Esto completa la vinculación, pero falta el ingrediente final, los puntos con estado entre los cuales animar, lo que también se conoce como fotogramas clave.
Fotogramas clave dinámicos
Existe una forma de animar con @scroll-timeline
declarativa y pura de CSS muy potente, pero la animación que elegí era demasiado dinámica. No hay manera de hacer una transición entre el ancho de auto
ni de crear de forma dinámica una cantidad de fotogramas clave según la longitud de los elementos secundarios.
Sin embargo, JavaScript sabe cómo obtener esa información, por lo que iteraremos sobre los elementos secundarios y tomaremos los valores calculados durante el tiempo de ejecución:
tabindicator.animate({
transform: [...tabnavitems].map(({offsetLeft}) =>
`translateX(${offsetLeft}px)`),
width: [...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
Para cada tabnavitem
, desestructura la posición de offsetLeft
y muestra una cadena que la use como un valor translateX
. De esta manera, se crean 4 fotogramas clave de transformación para la animación. Lo mismo ocurre con el ancho: a cada uno se le pregunta cuál es su ancho dinámico y, luego, se usa como valor de fotograma clave.
Este es un resultado de ejemplo, basado en mis fuentes y preferencias del navegador:
Fotogramas clave de TranslateX:
[...tabnavitems].map(({offsetLeft}) =>
`translateX(${offsetLeft}px)`)
// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]
Ancho de fotogramas clave:
[...tabnavitems].map(({offsetWidth}) =>
`${offsetWidth}px`)
// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]
Para resumir la estrategia, el indicador de pestaña ahora se animará en 4 fotogramas clave según la posición del ajuste de desplazamiento de la barra de desplazamiento de la sección. Estos puntos de ajuste crean una delimitación clara entre nuestros fotogramas clave y realmente contribuyen a la sensación de sincronización de la animación.
![La pestaña activa y la inactiva se muestran con superposiciones de VisBug que muestran puntuaciones de contraste aprobadas para ambas.](https://web.dev/static/articles/building/a-tabs-component/image/active-tab-inactive-tab-9604191ea7007.png?authuser=6&hl=es)
El usuario controla la animación con su interacción y ve que el ancho y la posición del indicador cambian de una sección a la siguiente, y hace un seguimiento perfecto con el desplazamiento.
Quizás no lo hayas notado, pero estoy muy orgullosa de la transición de color a medida que se selecciona el elemento de navegación destacado.
El gris más claro no seleccionado aparece aún más retrocedido cuando el elemento destacado tiene más contraste. Es común hacer la transición del color para el texto, como cuando se coloca el cursor sobre él y cuando se selecciona, pero el siguiente nivel es realizar la transición de ese color cuando se desplaza, sincronizada con el indicador de subrayado.
Así es como lo hice:
tabnavitems.forEach(navitem => {
navitem.animate({
color: [...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
}, {
duration: 1000,
fill: 'both',
timeline: sectionScrollTimeline,
}
);
});
Cada vínculo de navegación de pestañas necesita esta nueva animación de color y hace un seguimiento del mismo cronograma de desplazamiento que el indicador de subrayado. Uso la misma línea de tiempo que antes: como su función es emitir una marca en el desplazamiento, podemos usarla en cualquier tipo de animación que queramos. Como hice antes, creo 4 fotogramas clave en el bucle y devuelvo los colores.
[...tabnavitems].map(item =>
item === navitem
? `var(--text-active-color)`
: `var(--text-color)`)
// results in 4 array items, which represent 4 keyframe states
// [
"var(--text-active-color)",
"var(--text-color)",
"var(--text-color)",
"var(--text-color)",
]
El fotograma clave con el color var(--text-active-color)
destaca el vínculo y, de lo contrario, es de un color de texto estándar. Este bucle anidado lo hace relativamente sencillo, ya que el bucle externo corresponde a cada elemento de navegación y el bucle interno corresponde a los fotogramas clave personales de cada uno de ellos. Verifico si el elemento del bucle externo es el mismo que el del bucle interno y lo uso para saber cuándo está seleccionado.
Me divertí mucho escribiendo esto. Mucho.
Aún más mejoras de JavaScript
Vale la pena recordar que el núcleo de lo que te muestro aquí funciona sin JavaScript. Dicho esto, veamos cómo podemos mejorarlo cuando JS esté disponible.
Vínculos directos
Los vínculos directos son más bien un término para dispositivos móviles, pero creo que la intención del vínculo directo se cumple aquí con pestañas en las que puedes compartir una URL directamente con el contenido de una pestaña. El navegador in-page navegará al ID que coincida en el hash de la URL. Descubrí que este controlador onload
tuvo efecto en todas las plataformas.
window.onload = () => {
if (location.hash) {
tabsection.scrollLeft = document
.querySelector(location.hash)
.offsetLeft;
}
}
Sincronización del extremo de desplazamiento
Nuestros usuarios no siempre hacen clic o usan un teclado, a veces solo tienen desplazamiento libre, como deberían hacerlo. Cuando el desplazador de sección deja de desplazarse, su lugar de destino debe coincidir con la barra de navegación superior.
Así es como espero el final del desplazamiento:
js
tabsection.addEventListener('scroll', () => {
clearTimeout(tabsection.scrollEndTimer);
tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100);
});
Cuando el usuario se desplace por las secciones, borra el tiempo de espera correspondiente, si es que existe, y comienza uno nuevo. Cuando las secciones dejan de desplazarse, no borras el tiempo de espera y se activan 100 ms después de descansar. Cuando se activa, llama a una función para averiguar dónde se detuvo el usuario.
const determineActiveTabSection = () => {
const i = tabsection.scrollLeft / tabsection.clientWidth;
const matchingNavItem = tabnavitems[i];
matchingNavItem && setActiveTab(matchingNavItem);
};
Suponiendo que el desplazamiento se ajustó, la división de la posición actual de desplazamiento del ancho del área de desplazamiento debería dar como resultado un número entero y no un decimal. Luego, intento tomar un navitem de nuestra caché a través de este índice calculado y, si encuentra algo, envío la coincidencia para que se active.
const setActiveTab = tabbtn => {
tabnav
.querySelector(':scope a[active]')
.removeAttribute('active');
tabbtn.setAttribute('active', '');
tabbtn.scrollIntoView();
};
Para configurar la pestaña activa, primero se borra cualquier pestaña activa y, luego, se le otorga al elemento de navegación entrante el atributo de estado activo. La llamada a scrollIntoView()
tiene una interacción divertida con CSS que vale la pena destacar.
.scroll-snap-x {
overflow: auto hidden;
overscroll-behavior-x: contain;
scroll-snap-type: x mandatory;
@media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
En el CSS de la utilidad de ajuste de desplazamiento horizontal, anidamos una consulta de medios que aplica el desplazamiento smooth
si el usuario es tolerante al movimiento. JavaScript puede hacer llamadas libremente para desplazar elementos a la vista, y CSS puede administrar la UX de forma declarativa.
Es la maravillosa combinación que a veces hacen.
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías? Esto hace que la arquitectura de componentes sea divertida. ¿Quién creará la 1a versión con ranuras en su framework favorito? 🙂
Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crea una Glitch, twittea tu versión y la agregaré a la sección de remixes de la comunidad a continuación.
Remixes de la comunidad
- @devnook, @rob_dodson y @DasSurma con componentes web: artículo.
- @jhvanderschee con botones: CodePen.