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

Présentation de base de la création de composants FAB adaptatifs aux couleurs, responsifs et accessibles.

Dans cet article, je souhaite partager mes réflexions sur la façon de créer des composants FAB adaptatifs aux couleurs, responsifs et accessibles. Essayez la démo et consultez la source.

Si vous préférez les vidéos, voici une version YouTube de cet article :

Présentation

Les FAB sont plus courants sur mobile que sur ordinateur, mais ils sont présents dans les deux scénarios. Ils permettent de garder les actions principales à portée de vue, ce qui les rend pratiques et omniprésentes. Ce style d'expérience utilisateur a été rendu célèbre par Material UI. Vous trouverez des suggestions d'utilisation et d'emplacement ici.

É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 FAB dans la fenêtre d'affichage et gère un espace entre les boutons. Les boutons peuvent être petits ou par défaut, ce qui offre une belle variété entre les actions principales et secondaires.

Conteneur de bouton d'action flottant

Cet élément peut être un <div> normal, mais faisons plaisir à nos utilisateurs malvoyants et ajoutons-lui des attributs utiles pour expliquer l'objectif et le contenu de ce conteneur.

Balisage des FAB

Commencez par une classe .fabs pour que le CSS puisse s'y accrocher pour le style, puis ajoutez role="group" et aria-label pour qu'il ne s'agisse pas seulement d'un conteneur générique, mais d'un conteneur nommé et utile.

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

Style des boutons d'action flottants

Pour être pratiques, les FAB restent toujours dans la fenêtre d'affichage. C'est un excellent cas d'utilisation pour la position fixed. Dans cette position de fenêtre d'affichage, j'ai choisi d'utiliser inset-block et inset-inline afin que la position s'adapte au mode de document de l'utilisateur, comme de droite à gauche ou de gauche à droite. Les propriétés personnalisées sont également utilisées pour éviter les répétitions et garantir une distance égale par rapport aux 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);
}

Ensuite, je définis l'affichage du conteneur sur flex et je modifie la direction de sa mise en page sur column-reverse. Cela empile les enfants les uns sur les autres (colonne) et inverse également leur ordre visuel. Cela a pour effet de faire de l'élément sélectionnable le plus bas l'élément sélectionné en premier, au lieu de l'élément le plus haut, qui serait normalement sélectionné en premier selon le document HTML. L'inversion de l'ordre visuel unifie l'expérience pour les utilisateurs voyants et les utilisateurs de clavier. En effet, le style de l'action principale, plus grand que celui des mini-boutons, indique aux utilisateurs voyants qu'il s'agit d'une action principale, et les utilisateurs de clavier la sélectionneront comme premier élément de la source.

Deux boutons d&#39;action flottants sont affichés, avec les outils de développement superposant leur mise en page en grille. Affiche l&#39;écart entre eux avec un motif rayé, 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 d'action flottants

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

FAB par défaut

Le premier bouton à styliser est le bouton par défaut. Il servira de base à tous les boutons d'action flottants. Plus tard, nous créerons une variante qui permettra d'obtenir une apparence différente tout 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, car il offre une excellente expérience utilisateur avec la souris, le clavier et l'écran tactile. L'aspect le plus crucial de ce balisage est de masquer l'icône aux utilisateurs de lecteurs d'écran avec aria-hidden="true" et d'ajouter le texte du libellé nécessaire au balisage <button> lui-même. Lorsque j'ajoute des libellés dans ces cas, j'aime aussi ajouter un title pour que les utilisateurs de souris puissent obtenir des informations sur ce que l'icône essaie de 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

Commençons par transformer le bouton en un bouton rond rembourré avec une ombre forte, car ce sont les premières caractéristiques qui définissent le bouton :

.fab {
  --_size: 2rem;

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

Ajoutons maintenant de la couleur. Nous allons utiliser une stratégie que nous avons déjà utilisée dans les défis d'interface utilisateur. Créez un ensemble de propriétés personnalisées clairement nommé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 de la préférence de l'utilisateur pour les couleurs dans son système :

.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 que les icônes SVG s'adaptent à l'espace.

.fab {
  

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

Enfin, supprimez la mise en surbrillance du bouton au moment de l'appui, car nous avons ajouté notre propre retour visuel pour l'interaction :

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

Mini FAB

L'objectif de cette section est de créer une variante pour le bouton FAB. En réduisant la taille de certains FAB par rapport à l'action par défaut, nous pouvons mettre en avant l'action que l'utilisateur effectue le plus souvent.

Balisage du bouton d'action flottant petit

Le code HTML est le même que celui d'un FAB, mais nous ajoutons une classe ".mini" pour donner au CSS un point d'accroche dans 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 de 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 d&#39;action flottants empilés, le bouton supérieur étant plus petit que celui du bas.

Accessibilité

Le point le plus important à retenir concernant l'accessibilité des FAB est leur emplacement dans le flux de clavier de la page. Cette démo ne contient que des FAB. Il n'y a rien à concurrencer en termes d'ordre et de flux de clavier, ce qui signifie qu'elle n'a pas l'occasion de démontrer un flux de clavier significatif. Dans un scénario où plusieurs éléments se font concurrence pour attirer l'attention, je vous suggère de réfléchir attentivement à l'endroit où, dans ce flux, un utilisateur devrait se retrouver dans le flux du bouton FAB.

Démonstration de l'interaction avec le clavier

Une fois que l'utilisateur a sélectionné le conteneur FAB, nous avons déjà ajouté role="group" et aria-label="floating action buttons", qui informent les utilisateurs de lecteurs d'écran sur le contenu de ce qu'ils ont sélectionné. D'un point de vue stratégique, j'ai placé le bouton d'action flottant par défaut en premier pour que les utilisateurs trouvent l'action principale en premier. J'utilise ensuite flex-direction: column-reverse; pour ordonner visuellement le bouton principal en bas, près des doigts de l'utilisateur pour un accès facile. C'est une bonne chose, car le bouton par défaut est visuellement mis en avant et est également le 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 aux utilisateurs de lecteurs d'écran et de leur fournir un libellé pour le bouton afin qu'il ne soit pas une énigme. Cela a déjà été fait dans le HTML avec aria-hidden="true" sur les <svg> et aria-label="Some action" sur les <button>.

Animation

Vous pouvez ajouter différents types d'animations pour améliorer l'expérience utilisateur. Comme dans les autres défis d'interface utilisateur graphique, nous allons configurer quelques propriétés personnalisées pour contenir l'intention d'une expérience de mouvement réduit 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 permet ensuite de remplacer 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 variables contiennent des transitions appropriées en fonction des préférences de l'utilisateur, et la dernière variable --_transition sera définie sur --_motion-reduced ou --_motion-ok, respectivement.

.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 les éléments ci-dessus en place, les modifications apportées à box-shadow, background-color, transform et outline-offset peuvent être transférées, ce qui permet à l'utilisateur de recevoir un retour d'information agréable de l'UI indiquant que son interaction a été reçue.

Ensuite, ajoutez un peu plus de style à l'état :active en ajustant légèrement translateY. Cela donne au bouton un bel effet de pression :

.fab {
  

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

Enfin, effectuez la transition des modifications apportées aux 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 j'ai fait, comment feriez-vous ? 🙂

Diversifions nos approches et découvrons toutes les façons de créer sur le Web.

Créez une démo, tweetez-moi les liens et je l'ajouterai à la section des remix de la communauté ci-dessous !

Remix de la communauté

Aucun élément à afficher pour le moment.

Ressources