Cómo compilar un componente de botón de acción flotante (BAF)

Una descripción general básica de cómo compilar componentes de BAF accesibles, responsivos y adaptables al color.

En esta publicación, quiero compartir mis ideas sobre cómo compilar componentes de BAF accesibles, responsivos y adaptables al color. Prueba la demostración y consulta el código fuente.

Si prefieres un video, aquí tienes una versión de este artículo en YouTube:

Descripción general

Los FAB son más comunes en dispositivos móviles que en computadoras de escritorio, pero son frecuentes en ambos casos. Mantienen las acciones principales a la vista, lo que las hace convenientes y omnipresentes. Este estilo de experiencia del usuario se hizo famoso gracias a Material UI, 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 pequeños o predeterminados, lo que ofrece una buena variedad entre las acciones principales y secundarias.

Contenedor del BAF

Este elemento puede ser un <div> normal, pero hagamos un favor a nuestros usuarios con discapacidad visual y etiquetémoslo 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 al diseño 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, permanecen dentro del viewport en todo momento. Este es un excelente caso de uso para fixed. Dentro de esta posición de la ventana gráfica, 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 desde los bordes inferior y laterales de la ventana gráfica:

.fabs {
  --_viewport-margin: 2.5vmin;

  position: fixed;
  z-index: var(--layer-1);

  inset-block: auto var(--_viewport-margin);
  inset-inline: auto var(--_viewport-margin);
}

Luego, le doy al contenedor la visualización 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 sería donde el enfoque va normalmente según el documento HTML. Invertir el orden visual unifica la experiencia para los usuarios con visión y los usuarios de teclado, ya que el diseño de la acción principal como más grande que los botones pequeños indica a los usuarios con visión que es una acción principal, y los usuarios de teclado la enfocarán como el primer elemento de la fuente.

Se muestran dos botones de acción flotante con la superposición de Herramientas para desarrolladores en su diseño de cuadrícula. Muestra la brecha entre ellos con un patrón de rayas y también muestra su altura y ancho calculados.

.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 colocados en el contenedor.

Botones de BAF

Es hora de diseñar algunos botones para que parezca que flotan sobre todo.

BAF predeterminada

El primer botón que se debe diseñar es el botón predeterminado. Esta será la base de todos los botones de acción flotante. Más adelante, crearemos una variante que logre una apariencia alternativa y modifique la menor cantidad posible de estos estilos básicos.

Lenguaje de marcado del BAF

El elemento <button> es la opción correcta. Comenzaremos con esta opción como base, ya que ofrece una excelente experiencia del usuario con el mouse, el teclado y la pantalla táctil. El aspecto más importante de este marcado es ocultar el ícono a los usuarios de lectores de pantalla con aria-hidden="true" y agregar el texto de etiqueta necesario al marcado <button>. Cuando agrego etiquetas en estos casos, también me gusta agregar un title para que los usuarios de 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, convirtamos el botón en un botón redondo con relleno y una sombra marcada, 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 los 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 u oscuras según la preferencia de color del sistema del usuario:

.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 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 elemento destacado de presión del botón, ya que agregamos nuestra propia respuesta visual 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 acción flotante. Si hacemos que algunos de los FAB sean más pequeños que la acción predeterminada, podemos promover la acción que el usuario realiza con mayor frecuencia.

Lenguaje de marcado del botón de acción pequeño

El código HTML es el mismo que el de un botón de acción flotante, pero agregamos una clase ".mini" para que CSS tenga 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 en la variable --_size.

.fab.mini {
  --_size: 1.25rem;
}

Captura de pantalla de los dos botones de acción flotante apilados, en la que el botón superior es más pequeño que el inferior.

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, por lo que 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 del teclado significativo. En una situación en la que hay elementos que compiten por el enfoque, sugiero pensar detenidamente en qué parte de ese flujo debería encontrarse un usuario cuando ingresa al flujo del botón de FAB.

Demostración de interacción con el teclado

Una vez que el usuario se enfoca en el contenedor del FAB, ya agregamos role="group" y aria-label="floating action buttons", que informan a los usuarios de lectores de pantalla sobre el contenido en el que se enfocaron. Desde el punto de vista estratégico, coloqué el BAF predeterminado primero para que los usuarios encuentren la acción principal primero. 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. Esto es un gran logro, ya que el botón predeterminado se destaca visualmente y también es el primero para los usuarios de 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, de modo que no sea un misterio. Esto ya se hizo en el HTML con aria-hidden="true" en el <svg> y aria-label="Some action" en los <button>.

Animación

Se pueden agregar varios tipos de animaciones para mejorar la experiencia del usuario. Al igual que en otros desafíos de la GUI, configuraremos algunas propiedades personalizadas para mantener la intención de una experiencia de movimiento reducido y una experiencia de movimiento completo. De forma predeterminada, los diseños supondrán que el usuario desea un movimiento reducido y, luego, con la consulta de medios prefers-reduced-motion, intercambiarán el valor de transición por movimiento completo.

Una estrategia de movimiento reducido con propiedades personalizadas

En el siguiente código CSS, se crean tres propiedades personalizadas: --_motion-reduced, --_motion-ok y --_transition. Las dos primeras contienen transiciones adecuadas 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 a los cambios en box-shadow, background-color, transform y outline-offset, lo que le brinda al usuario comentarios visuales agradables sobre la recepción de su interacción.

A continuación, agrega un poco más de estilo al estado :active ajustando translateY un poco. Esto le da al botón un efecto de presión agradable:

.fab {
  

  &:active {
    @media (prefers-reduced-motion: no-preference) {
      transform: translateY(2%);
    }
  }
}

Por último, haz 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 crear contenido en la Web.

Crea una demostración, envíame por Twitter los vínculos y la agregaré a la sección de remixes de la comunidad que se encuentra a continuación.

Remixes de la comunidad

Aún no hay nada para ver aquí.

Recursos