Una descripción general fundamental sobre cómo compilar componentes de BAF responsivos, accesibles y adaptables al color.
En esta publicación, quiero compartir mis ideas sobre cómo compilar componentes de BAF responsivos, accesibles y adaptables al color. Prueba la demo y consulta la fuente.
Si prefieres ver un video, aquí tienes una versión de esta publicación en YouTube:
Descripción general
Los FAB son más comunes en dispositivos móviles que en computadoras, pero son frecuentes en ambos casos. Mantienen las acciones principales a la vista, lo que las hace convenientes y omnipresentes. Material UI popularizó este estilo de experiencia del usuario, y sus sugerencias de uso y ubicación se pueden encontrar aquí.
Elementos y estilos
El código HTML de estos controles incluye un elemento contenedor y un conjunto de uno o más botones. El contenedor posiciona los FAB dentro del viewport y administra un espacio entre los botones. Los botones pueden ser mini o predeterminados, lo que brinda una buena variedad entre las acciones principales y secundarias.
Contenedor de BAF
Este elemento puede ser un <div>
normal, pero hagamos un favor a nuestros usuarios con discapacidad visual y etiquétalo con algunos atributos útiles para explicar el propósito y el contenido de este contenedor.
Lenguaje de marcado de los BAF
Comienza con una clase .fabs
para que CSS se conecte para aplicar estilo y, luego, agrega role="group"
y aria-label
para que no sea solo un contenedor genérico, sino que tenga un nombre y un propósito.
<div class="fabs" role="group" aria-label="Floating action buttons">
<!-- buttons will go here -->
</div>
Estilo de los BAF
Para que los FAB sean convenientes, se mantienen dentro del viewport en todo momento.
Este es un excelente caso de uso para la posición fixed
. Dentro de esta posición del viewport, elegí usar inset-block
y inset-inline
para que la posición complemente el modo de documento del usuario, como de derecha a izquierda o de izquierda a derecha. Las propiedades personalizadas también se usan para evitar la repetición y garantizar una distancia igual de los bordes inferior y lateral del viewport:
.fabs {
--_viewport-margin: 2.5vmin;
position: fixed;
z-index: var(--layer-1);
inset-block: auto var(--_viewport-margin);
inset-inline: auto var(--_viewport-margin);
}
A continuación, le asigno la pantalla del contenedor flex
y cambio su dirección de diseño a column-reverse
.
Esto apila los elementos secundarios uno encima del otro (columna) y también invierte su orden visual. Esto tiene el efecto de hacer que el primer elemento enfocable sea el elemento inferior en lugar del superior, que es donde se enfoca normalmente según el documento HTML. Invertir el orden visual une la experiencia de los usuarios con visión y los usuarios del teclado, ya que el diseño de la acción principal es más grande que los botones en miniatura, lo que indica a los usuarios con visión que es una acción principal, y los usuarios del teclado la enfocarán como el primer elemento de la fuente.
.fabs {
…
display: flex;
flex-direction: column-reverse;
place-items: center;
gap: var(--_viewport-margin);
}
El centrado se controla con place-items
, y gap
agrega espacio entre los botones de BAF que se colocan en el contenedor.
Botones de BAF
Es hora de aplicar diseño a algunos botones para que parezcan estar flotando sobre todo.
FAB predeterminado
El primer botón al que se le aplica diseño es el predeterminado. Esto servirá como base para todos los botones de FAB. Más adelante, crearemos una variante que logre una apariencia alternativa mientras se modifican lo menos posible estos estilos básicos.
Lenguaje de marcado del BAF
El elemento <button>
es la opción correcta. Comenzaremos con esto como base porque ofrece una excelente experiencia del usuario con el mouse, la pantalla táctil y el teclado. El aspecto más crucial de este marcado es ocultar el ícono a los usuarios de lectores de pantalla con aria-hidden="true"
y agregar el texto de la etiqueta necesario al marcado <button>
. Cuando agrego etiquetas en estos casos, también me gusta agregar un title
para que los usuarios del mouse puedan obtener información sobre lo que el ícono intenta comunicar.
<button data-icon="plus" class="fab" title="Add new action" aria-label="Add new action">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>
Estilo del BAF
Primero, convertiremos el botón en un botón redondo acolchado con una sombra fuerte, ya que estas son las primeras características definitorias del botón:
.fab {
--_size: 2rem;
padding: calc(var(--_size) / 2);
border-radius: var(--radius-round);
aspect-ratio: 1;
box-shadow: var(--shadow-4);
}
A continuación, agreguemos color. Usaremos una estrategia que ya usamos en desafíos de GUI. Crea un conjunto de propiedades personalizadas con un nombre claro que contenga de forma estática los colores claros y oscuros, y, luego, una propiedad personalizada adaptable que se establecerá en las variables claras o oscuras según la preferencia del sistema del usuario para los colores:
.fab {
…
/* light button and button hover */
--_light-bg: var(--pink-6);
--_light-bg-hover: var(--pink-7);
/* dark button and button hover */
--_dark-bg: var(--pink-4);
--_dark-bg-hover: var(--pink-3);
/* adaptive variables set to light by default */
--_bg: var(--_light-bg);
/* static icon colors set to the adaptive foreground variable */
--_light-fg: white;
--_dark-fg: black;
--_fg: var(--_light-fg);
/* use the adaptive properties on some styles */
background: var(--_bg);
color: var(--_fg);
&:is(:active, :hover, :focus-visible) {
--_bg: var(--_light-bg-hover);
@media (prefers-color-scheme: dark) {
--_bg: var(--_dark-bg-hover);
}
}
/* if users prefers dark, set adaptive props to dark */
@media (prefers-color-scheme: dark) {
--_bg: var(--_dark-bg);
--_fg: var(--_dark-fg);
}
}
A continuación, agrega algunos estilos para ayudar a que los íconos SVG se ajusten al espacio.
.fab {
…
& > svg {
inline-size: var(--_size);
block-size: var(--_size);
stroke-width: 3px;
}
}
Por último, quita el resaltado de presión del botón, ya que agregamos nuestros propios comentarios visuales para la interacción:
.fab {
-webkit-tap-highlight-color: transparent;
}
BAF pequeño
El objetivo de esta sección es crear una variante para el botón de FAB. Si hacemos que algunos de los FAB sean más pequeños que la acción predeterminada, podemos promocionar la acción que el usuario realiza con más frecuencia.
Lenguaje de marcado de BAF pequeño
El código HTML es el mismo que el de un FAB, pero agregamos una clase ".mini" para darle al CSS un gancho en la variante.
<button data-icon="heart" class="fab mini" title="Like action" aria-label="Like action">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">...</svg>
</button>
Estilo de BAF pequeño
Gracias al uso de propiedades personalizadas, el único cambio necesario es un ajuste a la variable --_size
.
.fab.mini {
--_size: 1.25rem;
}
Accesibilidad
La parte más importante que se debe recordar para la accesibilidad con los FAB es la ubicación dentro del flujo del teclado de la página. Esta demostración solo tiene los FAB, no hay nada con lo que competir en términos de orden y flujo del teclado, lo que significa que no tiene la oportunidad de demostrar un flujo de teclado significativo. En una situación en la que hay elementos que compiten por el enfoque, te sugiero que pienses detenidamente en qué parte de ese flujo debería encontrarse un usuario para ingresar al flujo del botón de FAB.
Una vez que el usuario enfocó el contenedor de FAB, ya agregamos role="group"
y aria-label="floating action buttons"
, que informan a los usuarios del lector de pantalla sobre el contenido en el que se enfocaron. De manera estratégica, coloqué primero el BAF predeterminado para que los usuarios encuentren primero la acción principal. Luego, uso flex-direction: column-reverse;
para ordenar visualmente el botón principal en la parte inferior, cerca de los dedos de los usuarios para facilitar el acceso. Esta es una buena ventaja, ya que el botón predeterminado es visualmente prominente y también es el primero para los usuarios del teclado, lo que les brinda experiencias muy similares.
Por último, no olvides ocultar los íconos a los usuarios de lectores de pantalla y asegúrate de proporcionarles una etiqueta para el botón para que no sea un misterio. Esto ya se hizo en el HTML con aria-hidden="true"
en <svg>
y aria-label="Some action"
en <button>
.
Animación
Se pueden agregar varios tipos de animaciones para mejorar la experiencia del usuario. Al igual que en otros desafíos de GUI, configuraremos un par de propiedades personalizadas para contener la intención de una experiencia de movimiento reducido y una experiencia de movimiento completo. De forma predeterminada, los estilos asumirán que el usuario desea reducir el movimiento y, luego, con la consulta de contenido multimedia prefers-reduced-motion
, se intercambiará el valor de transición a movimiento completo.
Una estrategia de movimiento reducido con propiedades personalizadas
Se crean tres propiedades personalizadas en el siguiente CSS: --_motion-reduced
, --_motion-ok
y --_transition
. Los dos primeros contienen transiciones apropiadas según la preferencia del usuario, y la última variable --_transition
se establecerá en --_motion-reduced
o --_motion-ok
, respectivamente.
.fab {
/* box-shadow and background-color can safely be transitioned for reduced motion users */
--_motion-reduced:
box-shadow .2s var(--ease-3),
background-color .3s var(--ease-3);
/* add transform and outline-offset for users ok with motion */
--_motion-ok:
var(--_motion-reduced),
transform .2s var(--ease-3),
outline-offset 145ms var(--ease-2);
/* default the transition styles to reduced motion */
--_transition: var(--_motion-reduced);
/* set the transition to our adaptive transition custom property*/
transition: var(--_transition);
/* if motion is ok, update the adaptive prop to the respective transition prop */
@media (prefers-reduced-motion: no-preference) {
--_transition: var(--_motion-ok);
}
}
Con lo anterior, se pueden realizar transiciones en los cambios de box-shadow
, background-color
, transform
y outline-offset
, lo que le brinda al usuario buenos comentarios de la IU que indican que se recibió su interacción.
A continuación, agrega un poco más de estilo al estado :active
ajustando un poco translateY
. Esto le da al botón un buen efecto de presión:
.fab {
…
&:active {
@media (prefers-reduced-motion: no-preference) {
transform: translateY(2%);
}
}
}
Por último, realiza la transición de los cambios a los íconos SVG en los botones:
.fab {
…
&[data-icon="plus"]:hover > svg {
transform: rotateZ(.25turn);
}
& > svg {
@media (prefers-reduced-motion: no-preference) {
will-change: transform;
transition: transform .5s var(--ease-squish-3);
}
}
}
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías tú? 🙂
Diversifiquemos nuestros enfoques y aprendamos todas las formas de compilar en la Web.
Crea una demo, twittea los vínculos y los agregaré a la sección de remixes de la comunidad a continuación.
Remixes de la comunidad
Aún no hay nada que ver.
Recursos
- Código fuente en GitHub