Créer un composant de bouton d'action flottant (FAB)

Présentation des principes de base sur la création de composants d'action flottants adaptatifs, adaptables et accessibles aux couleurs.

Dans cet article, je souhaite partager mes réflexions sur la création de composants FAB adaptatifs, responsifs et accessibles. Essayez la démonstration et consultez la source.

Si vous préférez la vidéo, voici une version YouTube de ce post:

Présentation

Les boutons d'action flottants sont plus courants sur mobile que sur ordinateur, mais ils sont fréquents dans les deux scénarios. Elles permettent de voir les actions principales, ce qui les rend pratiques et omniprésentes. Ce style d'expérience utilisateur a été rendu célèbre par Material UI. Les suggestions d'utilisation et d'emplacement sont disponibles sur cette page.

Éléments et styles

Le code HTML de ces commandes implique un élément de conteneur et un ensemble d'un ou plusieurs boutons. Le conteneur positionne les boutons d'action flottants dans la fenêtre d'affichage et gère l'espace entre les boutons. Les boutons peuvent être de taille mini ou par défaut, ce qui offre une certaine variété entre les actions principales et secondaires.

Conteneur du bouton d'action flottant

Cet élément peut être un <div> standard, mais rendons service à nos utilisateurs non voyants en leur ajoutant des attributs utiles pour expliquer l'objectif et le contenu de ce conteneur.

Balisage des boutons d'action flottants

Commencez par une classe .fabs permettant au CSS de s'associer pour plus de style, puis ajoutez role="group" et aria-label pour qu'il ne s'agisse pas d'un simple conteneur générique : il est nommé et pertinent.

<div class="fabs" role="group" aria-label="Floating action buttons">
  <!-- buttons will go here -->
</div>

Style des boutons d'action flottants

Pour que les boutons d'action flottants soient pratiques, ils restent affichés en permanence dans la fenêtre d'affichage. Il s'agit d'un excellent cas d'utilisation de la position fixed. Dans cette position de fenêtre d'affichage, j'ai choisi d'utiliser inset-block et inset-inline afin de compléter le mode document de l'utilisateur, par exemple de droite à gauche ou de gauche à droite. Les propriétés personnalisées permettent également d'éviter les répétitions et de garantir une distance égale entre les bords inférieur et latéral de la fenêtre d'affichage:

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

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

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

Je définis ensuite l'affichage flex du conteneur et je change l'orientation de la mise en page sur column-reverse. Les enfants sont alors empilés les uns sur les autres (colonne) et inverse leur ordre visuel. Cela a pour effet de faire du premier élément sélectionnable l'élément inférieur plutôt que le haut, qui est l'endroit où le focus va normalement pour le document HTML. Inverser l'ordre visuel permet de rendre l'expérience unique pour les utilisateurs voyants et les utilisateurs de clavier. En effet, le style de l'action principale, plus grand que les mini-boutons, indique aux personnes voyantes qu'il s'agit d'une action principale, et les utilisateurs du clavier la placent en premier dans la source.

Deux boutons d&#39;action flottant sont affichés, les outils de développement se superposant à leur mise en page sous forme de grille. Affiche l&#39;écart entre les deux sous la forme d&#39;un motif à rayures, ainsi que leur hauteur et leur largeur calculées.

.fabs {
  …

  display: flex;
  flex-direction: column-reverse;
  place-items: center;
  gap: var(--_viewport-margin);
}

Le centrage est géré avec place-items, et gap ajoute de l'espace entre les boutons d'action flottants placés dans le conteneur.

Boutons du bouton d'action flottant

Il est temps de styliser certains boutons pour qu'ils flottent au-dessus de tout.

Bouton d'action flottant par défaut

Le premier bouton auquel appliquer un style est le bouton par défaut. Il servira de base pour tous les boutons d'action flottants. Par la suite, nous créerons une variante qui donne une autre apparence en modifiant le moins possible ces styles de base.

Balisage du bouton d'action flottant

L'élément <button> est le bon choix. Nous allons commencer par celui-ci comme base, car il offre une excellente expérience utilisateur avec la souris, l'écran tactile et le clavier. L'aspect le plus crucial de ce balisage consiste à masquer l'icône pour les utilisateurs de lecteurs d'écran avec aria-hidden="true" et à ajouter le texte de libellé nécessaire au balisage <button> lui-même. Dans ce cas, lors de l'ajout d'étiquettes, j'aime également ajouter un élément title pour que les utilisateurs de souris puissent obtenir des informations sur ce que l'icône espère communiquer.

<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>

Style du bouton d'action flottant

Tout d'abord, transformons le bouton en un bouton rond rempli avec une ombre forte, car voici les premières caractéristiques définissant le bouton:

.fab {
  --_size: 2rem;

  padding: calc(var(--_size) / 2);
  border-radius: var(--radius-round);
  aspect-ratio: 1;
  box-shadow: var(--shadow-4);
}

Ajoutons ensuite de la couleur. Nous utiliserons une stratégie que nous avons utilisée dans les défis de l'interface graphique auparavant. Créez un ensemble clairement nommé de propriétés personnalisées qui contiennent de manière statique les couleurs claires et sombres, puis une propriété personnalisée adaptative qui sera définie sur les variables claires ou sombres en fonction des préférences système de l'utilisateur pour les couleurs:

.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);
  }
}

Ajoutez ensuite des styles pour aider les icônes SVG à s'adapter à l'espace.

.fab {
  …

  & > svg {
    inline-size: var(--_size);
    block-size: var(--_size);
    stroke-width: 3px;
  }
}

Enfin, supprimez la mise en surbrillance du bouton, car nous avons ajouté notre propre commentaire visuel pour les interactions:

.fab {
  -webkit-tap-highlight-color: transparent;
}

Mini bouton d'action flottant

L'objectif de cette section est de créer une variante du bouton d'action flottant. En réduisant la taille de certains boutons d'action flottants par rapport à l'action par défaut, nous pouvons promouvoir l'action que l'utilisateur effectue le plus souvent.

Balisage du mini bouton d'action flottant

Le code HTML est identique à un bouton d'action flottant, mais nous ajoutons une classe ".mini" pour permettre au code CSS d'interagir avec 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>
Style du mini bouton d'action flottant

Grâce à l'utilisation de propriétés personnalisées, la seule modification nécessaire est un ajustement de la variable --_size.

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

Capture d&#39;écran des deux boutons du bouton d&#39;action flottant empilés, et le bouton du haut est plus petit que celui du bas.

Accessibilité

La partie la plus importante à garder à l'esprit pour l'accessibilité avec les boutons d'action flottants est le placement dans le flux clavier de la page. Cette démonstration ne présente que les boutons d'action flottants. Il n'y a rien à comparer en termes d'ordre et de fluidité du clavier, ce qui signifie qu'elle n'a pas la possibilité de montrer une fluidité de clavier pertinente. Dans un scénario où la mise au point s'accompagne d'éléments concurrents, je vous conseille de réfléchir en détail à l'endroit où l'utilisateur doit se retrouver pour accéder au bouton du bouton d'action flottant.

Démonstration des interactions avec le clavier

Une fois que l'utilisateur a sélectionné le conteneur du bouton d'action flottant, nous avons déjà ajouté role="group" et aria-label="floating action buttons", qui informent les utilisateurs de lecteurs d'écran sur le contenu des éléments sélectionnés. J'ai stratégiquement placé le bouton d'action flottant par défaut en premier, afin que les utilisateurs trouvent l'action principale en premier. Ensuite, j'utilise flex-direction: column-reverse; pour organiser visuellement le bouton principal en bas, à proximité des doigts de l'utilisateur, pour un accès facile. C'est une belle victoire, car le bouton par défaut est visuellement proéminent et en premier pour les utilisateurs de clavier, ce qui leur offre des expériences très similaires.

Enfin, n'oubliez pas de masquer vos icônes pour les utilisateurs de lecteurs d'écran et de leur attribuer une étiquette pour le bouton afin qu'il ne soit pas un mystère. Cette opération a déjà été effectuée dans le code HTML avec aria-hidden="true" sur <svg> et aria-label="Some action" sur les <button>.

Animation

Différents types d'animations peuvent être ajoutés pour améliorer l'expérience utilisateur. Comme dans d'autres défis de l'IUG, nous allons configurer quelques propriétés personnalisées pour contenir l'intention d'une expérience de mouvement réduite et d'une expérience de mouvement complet. Par défaut, les styles supposent que l'utilisateur souhaite réduire les mouvements. La requête média prefers-reduced-motion remplace alors la valeur de transition par un mouvement complet.

Stratégie de réduction des mouvements avec des propriétés personnalisées

Trois propriétés personnalisées sont créées dans le CSS suivant: --_motion-reduced, --_motion-ok et --_transition. Les deux premières contiennent les transitions appropriées en fonction des préférences de l'utilisateur. La dernière variable --_transition est définie respectivement sur --_motion-reduced ou --_motion-ok.

.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);
  }
}

Une fois ce qui précède en place, les modifications apportées à box-shadow, background-color, transform et outline-offset peuvent être migrées, ce qui permet à l'utilisateur de savoir que son interaction a bien été reçue.

Ajoutez ensuite un peu plus d'élégance à l'état :active en ajustant légèrement translateY. Vous obtenez ainsi un joli effet d'appui sur le bouton:

.fab {
  …

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

Enfin, effectuez la transition des icônes SVG dans les boutons:

.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);
    }
  }
}

Conclusion

Maintenant que vous savez comment je l'ai fait, comment le feriez-vous‽ 😃 ?

Diversissons nos approches et apprenons toutes les façons de créer sur le Web.

Créez une démo, cliquez sur les liens tweet me, et je l'ajouterai à la section "Remix" de la communauté ci-dessous.

Remix de la communauté

Rien à afficher pour le moment.

Ressources