Una descripción general básica sobre cómo compilar un componente de pestañas similar a los que se encuentran en las apps para iOS y Android.
En esta publicación, compartiré ideas sobre cómo crear un componente de Pestañas para la Web. que sea responsiva, admita 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 tomar muchas formas y
formularios. Primero, había pestañas de escritorio compiladas en el elemento <frame>
, y ahora tenemos
Son componentes móviles eficaces que animan contenido basado en propiedades físicas.
Todos intentan lo mismo: ahorrar espacio.
Hoy en día, lo esencial de una experiencia del usuario con pestañas es un área de navegación con botones que activa o desactiva la visibilidad del contenido en un marco de visualización. Muchos áreas de contenido comparten el mismo espacio, pero se presentan de manera condicional según el botón seleccionado en el menú de navegación.
Tácticas web
Creo que este componente es bastante sencillo de compilar, gracias a un algunas funciones esenciales de la plataforma web:
scroll-snap-points
para interactuar con el teclado y deslizar de forma elegante posiciones adecuadas de parada de desplazamiento- Vínculos directos a través de hashes de URL para Admite el uso compartido y el anclado de desplazamiento de in-page controlado por el navegador
- Compatibilidad con lectores de pantalla con lenguaje de marcado de los elementos
<a>
yid="#hash"
prefers-reduced-motion
para habilitar transiciones de encadenado y funciones de desplazamiento en la página- La función web
@scroll-timeline
en borrador para subrayar y color que cambia la pestaña seleccionada
El código HTML
Básicamente, la UX aquí es: hacer clic en un vínculo, hacer que la URL represente el elemento anidado estado de la página y, luego, ver que el área de contenido se actualiza a medida que el navegador se desplaza a la elemento coincidente.
Hay algunos miembros de contenido estructural: vínculos y :target
. Mié
necesitas una lista de vínculos, para la cual es excelente un <nav>
, y una lista de <article>
elementos, para los que una <section>
es ideal. Cada hash de vínculo coincidirá con una sección,
y permitir que el navegador desplace por los elementos mediante el anclado.
Por ejemplo, si haces clic en un vínculo, se enfoca automáticamente el artículo :target
en
Chrome 89, no se requiere JS. Luego, el usuario puede desplazarse por el contenido del artículo
su dispositivo de entrada. Es contenido complementario, como se indica en el
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
son estas:
<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 mucha variedad de lorem y los enlaces con una títulos de longitud mixta y conjunto de imágenes. Con contenido con el que trabajar, podemos comenzar .
Diseños con desplazamiento
Este componente incluye 3 tipos diferentes de áreas de desplazamiento:
- La navegación (rosa) es horizontal desplazable
- El área de contenido (azul) está horizontalmente. desplazable
- Cada artículo (verde) se ve verticalmente desplazable.
El desplazamiento incluye 2 tipos diferentes de elementos:
- Una ventana
Un cuadro con dimensiones definidas que tiene eloverflow
estilo de propiedad. - Una superficie de gran tamaño
En este diseño, son los contenedores de lista: nav vínculos, artículos de la sección y contenido de los artículos.
Diseño <snap-tabs>
El diseño de nivel superior que elegí fue flexible (Flexbox). Establecí la dirección para
column
para que el encabezado y la sección se ordenen verticalmente. Esta es nuestra primera
de desplazamiento y se oculta todo lo que tiene el desbordamiento oculto. El encabezado y
empleará el sobredesplazamiento pronto, 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; } }
Volvamos al colorido diagrama de 3 desplazamientos:
<header>
ya está preparado para ser (rosa) de desplazamiento.<section>
está preparado para ser el desplazamiento (azul). contenedor.
Los marcos que destaqué a continuación con VisBug nos ayuda a ver las ventanas que que crearon los contenedores de desplazamiento.
Diseño de las pestañas <header>
El siguiente diseño es casi igual: 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; }
.snap-indicator
debe viajar de forma horizontal con el grupo de vínculos.
este diseño de encabezado ayuda a establecer esa etapa. Aquí no hay elementos de posicionamiento absoluto.
Luego, los estilos de desplazamiento. Resulta que podemos compartir los estilos de desplazamiento
entre las 2 áreas de desplazamiento horizontal (encabezado y sección), así que hice una
clase, .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, la contención de desplazamiento para atrapar el sobredesplazamiento, los barras de desplazamiento para dispositivos táctiles y, por último, ajuste de desplazamiento para bloquear contenido en áreas de presentación. Puedes acceder a nuestro orden de tabulación del teclado y a cualquier guía de interacción la concentración naturalmente. Los contenedores de instantáneas también tienen un bonito estilo de carrusel interacción desde el teclado.
Diseño del encabezado de pestañas <nav>
Los vínculos de navegación deben ubicarse en línea, sin saltos de línea, de forma vertical. centrado, y cada elemento de vínculo debe ajustarse al contenedor de ajuste de desplazamiento. Swift para 2021 CSS.
<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; } }
Cada vínculo tiene un estilo y un tamaño específicos, por lo que el diseño de navegación solo debe especificar dirección y flujo. Los anchos únicos en elementos de navegación hacen la transición entre pestañas. divertido mientras el indicador ajusta su ancho al nuevo objetivo. Según la cantidad se encuentran aquí, el navegador renderizará una barra de desplazamiento o no.
Diseño de las pestañas <section>
Esta sección es flexible y debe ser el consumidor dominante del espacio. Integra
también debe crear columnas en las que se colocarán los artículos. Nuevamente,
funcionan para CSS 2021. block-size: 100%
estira este elemento para llenar el
superior posible y, para su propio diseño, crea una serie de
que tienen 100%
del ancho del elemento superior. Los porcentajes funcionan muy bien aquí.
porque escribimos restricciones fuertes 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 dijéramos “expandirte verticalmente tanto como sea posible, de manera insistente”.
(recuerda el encabezado que establecimos en flex-shrink: 0
: es una defensa contra esto
empuje de expansión), que establece la altura de la fila para un conjunto de columnas de altura completa. El
El estilo auto-flow
le indica a la cuadrícula que siempre coloque los elementos secundarios en posición horizontal
línea, sin ajuste, exactamente lo que queremos; desborde la ventana principal.
A veces me cuesta entender lo que hago. Este elemento de la sección está encajaban en una caja, sino que también creé un conjunto de cajas. Espero que las imágenes y explicaciones te ayudan.
Diseño de las pestañas <article>
El usuario debe poder desplazarse por el contenido del artículo, y las barras de desplazamiento deben poder desplazarse por el contenido. solo debe aparecer si hay un desbordamiento. Estos elementos de artículo están en una posición. Son simultáneamente un elemento superior de desplazamiento y un elemento secundario de desplazamiento. El el navegador está manejando algunas interacciones complicadas con los controles táctiles, el mouse y el teclado para nosotros.
<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 ajusten a su barra de desplazamiento superior. Me gusta mucho la manera en que los elementos del vínculo de navegación y los elementos del artículo se ajustan al inicio intercalado de sus respectivos contenedores de desplazamiento. Tiene el aspecto y la sensación armoniosa relación.
El artículo es un elemento secundario de cuadrícula y su tamaño está predeterminado para que sea el viewport en la que queremos proporcionar la UX de desplazamiento. Esto significa que no necesito alto ni ancho estilos aquí, solo necesito definir cómo se desborda. Configuré overflow-y en automático, además de capturar las interacciones de desplazamiento con el útil comportamiento de sobredesplazamiento. propiedad.
Resumen de las 3 áreas de desplazamiento
A continuación, elegí "mostrar siempre las barras de desplazamiento" en la configuración del sistema. Creo es el doble de importante que el diseño funcione con esta configuración activada, ya que es para que revise el diseño y la organización de desplazamientos.
Creo que ver el margen de la barra de desplazamiento en este componente ayuda a mostrar claramente dónde las áreas de desplazamiento, la dirección que admiten y cómo interactúan con entre sí. Considera cómo cada uno de estos marcos de ventana de desplazamiento también son flexibles o elementos superiores de cuadrícula a un diseño.
Las Herramientas para desarrolladores pueden ayudarnos a visualizar esto:
Los diseños de desplazamiento están completos: ajuste, vinculación directa y teclado. accesible. Sólidas bases para mejoras de UX, estilo y deleite.
Función destacada
Los elementos secundarios ajustados al desplazamiento mantienen su posición bloqueada durante el cambio de tamaño. Esto significa JavaScript no tendrá que mostrar nada durante la rotación del dispositivo o el navegador. cambiar el tamaño. Pruébalo en el dispositivo Herramientas para desarrolladores de Chromium Modo de Selecciona cualquier modo que no sea Responsivo y, luego, cambia el tamaño del marco del dispositivo. Observa que el elemento permanece visible y bloqueado con su contenido. Esto se ha disponibles desde que Chromium actualizó su implementación para que coincidiera con la especificación. Aquí tienes una entrada de blog al respecto.
Animación
En este caso, el objetivo de la animación es vincular claramente las interacciones con la IU. comentarios. Esto ayuda a guiar o asistir al usuario en su (con suerte) descubrimiento fluido de todo el contenido. Agregaré movimiento con propósito y de forma condicional. Ahora los usuarios pueden especificar sus movimientos preferencias en su sistema operativo, y disfruto mucho de responder a sus preferencias en mis interfaces.
Vincularé una pestaña subrayada con la posición de desplazamiento del artículo. El ajuste no
solo una alineación atractiva, también fija el inicio y el final de una animación.
De esta manera, se conserva el elemento <nav>
, que actúa como un
mini-mapa, conectado al contenido.
Comprobaremos la preferencia de movimiento del usuario desde CSS y JS. Hay un
hay excelentes lugares para ser considerado.
Comportamiento de desplazamiento
Hay una oportunidad para mejorar el comportamiento de movimiento de :target
y
element.scrollIntoView()
De forma predeterminada, es un servicio instantáneo. El navegador solo configura la
posición de desplazamiento. ¿Y 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;
}
}
Como aquí presentamos movimientos y movimientos que el usuario no controla (como el desplazamiento), solo aplicamos este estilo si el usuario no tiene preferencia en su sistema operativo contra la reducción del movimiento. De esta manera, solo presentamos el desplazamiento, para quienes están de acuerdo con ello.
Indicador de pestañas
El propósito de esta animación es ayudar a asociar el indicador con el estado
del contenido. Decidí colorear los estilos border-bottom
encadenados para los usuarios
que prefieren el movimiento reducido y una animación de deslizamiento y atenuación de color vinculados mediante desplazamiento
para los usuarios que no tienen problemas con el movimiento.
En las Herramientas para desarrolladores de Chromium, puedo activar o desactivar la preferencia y demostrar los 2 diferentes estilos de transición. Me divertí mucho armando 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 la .snap-indicator
cuando el usuario prefiere menos movimiento, ya que yo no
ya no lo necesitarás. Luego, lo reemplazo por los estilos border-block-end
y un
transition
Observa también en la interacción de las pestañas que el elemento de navegación activo no
solo tiene un subrayado de marca destacado, pero el color del texto también es más oscuro. El
activo tiene un mayor contraste del color de texto y un acento brillante con poca luz.
Con unas pocas líneas adicionales de CSS, los usuarios se sentirán incluidos (en el sentido de que respetamos sus preferencias de movimiento). Me encanta.
@scroll-timeline
En la sección anterior, te mostré cómo manejo el encadenado de movimiento reducido y, en esta sección, te mostraré cómo vinculé el indicador y una el área de desplazamiento. A continuación, veremos algunas divertidas cosas experimentales. Espero que tan emocionada como yo.
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
);
Primero verifico la preferencia de movimiento del usuario desde JavaScript. Si el resultado de
es false
, lo que significa que el usuario prefiere un movimiento reducido, entonces no ejecutaremos ninguna.
de los efectos de movimiento de
vinculación de desplazamiento.
if (motionOK) {
// motion based animation code
}
Al momento de escribir esto, la compatibilidad de los navegadores para
@scroll-timeline
no es ninguno. Es un
especificación de borrador solo
implementaciones experimentales. Pero tiene un polyfill que uso aquí.
demostración.
ScrollTimeline
Aunque CSS y JavaScript pueden crear cronogramas de desplazamiento, habilité JavaScript para poder usar las mediciones de los 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 objeto siga la posición de desplazamiento de otro y que, al crear un
ScrollTimeline
: Defino el controlador del vínculo de desplazamiento, scrollSource
.
Normalmente, una animación en la Web se ejecuta frente a una marca de período global, pero
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,
señalar el seguidor del desplazamiento, tabindicator
, se animará con base en
en un cronograma personalizado, el pergamino de nuestra sección. Esto completa la vinculación, pero
falta el ingrediente final, puntos con estado entre los cuales animar, también conocidos como
los fotogramas clave.
Fotogramas clave dinámicos
Existe una manera muy poderosa y pura de usar CSS para animar
@scroll-timeline
, pero la animación que elegí era demasiado dinámica. No hay
manera de hacer la transición entre el ancho de auto
y no hay forma de crear
una serie de fotogramas clave según la longitud de los elementos secundarios.
Sin embargo, JavaScript sabe cómo obtener esa información, así que iteraremos el secundarios y tomar los valores calculados en 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 lo usa como un valor translateX
. Esto crea 4 fotogramas clave de transformación para la
animación. Lo mismo se hace 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)"]
Fotogramas clave de ancho:
[...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 cuatro fotogramas clave. según la posición de ajuste de desplazamiento de la barra de desplazamiento de la sección. Los puntos de ajuste crear una delimitación clara entre los fotogramas clave y mejorar la y sincronizada de la animación.
El usuario controla la animación a través de su interacción, viendo el ancho y del indicador cambia de una sección a la siguiente, realizando un seguimiento a la perfección con el desplazamiento.
Quizás no lo hayas notado, pero me enorgullece la transición del color se seleccionará el elemento de navegación destacado.
El gris más claro no está seleccionado aparece aún más retrocedido cuando el color tiene más contraste. Es común aplicar colores de transición para el texto, como colocar el cursor sobre ellos y cuando se selecciona, pero es el siguiente nivel hacer una transición de ese color al desplazarse, 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, que hace un seguimiento del mismo desplazamiento. la línea de tiempo como indicador de subrayado. Utilizo la misma línea de tiempo que antes, ya que, Su rol es emitir una marca al desplazarse, podemos usar esa marca en cualquier tipo de de animación que queramos. Como hice antes, creo 4 fotogramas clave en el bucle y vuelvo 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.
de lo contrario, será un color de texto estándar. El bucle anidado lo hace relativamente
sencillo, ya que el bucle externo es cada elemento de navegación, y el bucle interno es cada uno
los fotogramas clave personales de navitem. Verifico si el elemento de bucle externo es el mismo que
el bucle interno uno y úsalo para saber cuándo se selecciona.
Me divertí mucho escribiendo esto. Mucho.
Más mejoras de JavaScript
Vale la pena recordar que el núcleo de lo que te estoy mostrando aquí funciona sin JavaScript: Dicho esto, veamos cómo podemos mejorarlo cuando JS disponibles.
Vínculos directos
Los vínculos directos corresponden más a un término para dispositivos móviles, pero creo que el propósito de los vínculos directos es
se encuentran aquí con pestañas, ya que puedes compartir una URL directamente con el contenido de una pestaña. El
navegador navegará en la página al ID que coincide en el hash de la URL. encontré
este controlador onload
hizo el efecto en todas las plataformas.
window.onload = () => {
if (location.hash) {
tabsection.scrollLeft = document
.querySelector(location.hash)
.offsetLeft;
}
}
Sincronización final de desplazamiento
Nuestros usuarios no siempre usan los teclados o hacen clic en ellos. A veces, solo el desplazamiento libre, como deberían poder. Cuando se detiene la barra de desplazamiento de secciones el desplazamiento, donde sea que llegue debe coincidir en la barra de navegación superior.
Así es como espero que finalice el desplazamiento:
js
tabsection.addEventListener('scroll', () => {
clearTimeout(tabsection.scrollEndTimer);
tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100);
});
Cuando se desplacen las secciones, borra el tiempo de espera de la sección, si lo hay. y comenzar una nueva. Cuando las secciones dejan de desplazarse, no borres el tiempo de espera, y se activan 100 ms después de descansar. Cuando se active, llamar a la función que busque calcular donde el usuario se detuvo.
const determineActiveTabSection = () => {
const i = tabsection.scrollLeft / tabsection.clientWidth;
const matchingNavItem = tabnavitems[i];
matchingNavItem && setActiveTab(matchingNavItem);
};
Suponiendo que el desplazamiento está ajustado, dividiendo la posición de desplazamiento actual del ancho del área de desplazamiento debe dar como resultado un número entero y no un decimal. Luego, intento tomar un navitem desde nuestra caché a través de este índice calculado y si encuentra 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, se borra
cualquier pestaña que esté activa y luego se le otorga
el elemento de navegación entrante en 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 la utilidad de ajuste de desplazamiento horizontal de CSS,
anidadó una consulta de medios que aplica
Es el desplazamiento de smooth
si el usuario es tolerante al movimiento. JavaScript puede hacer
llamadas para desplazar elementos a la vista, y CSS puede administrar la UX de forma declarativa.
Es la pequeña coincidencia que hacen a veces.
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías? Esto hace que sea divertido de la arquitectura de componentes de componentes en la nube. ¿Quién hará la 1a versión con ranuras en su framework favorito? 🙂
Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crea Glitch y twittea. tu versión y la agregaré a los remixes de la comunidad a continuación.
Remixes de la comunidad
- @devnook, @rob_dodson y @DasSurma con componentes web: article.
- @jhvanderschee con botones: CodePen.