Creazione di un componente Pulsante di azione floating (FAB)

Una panoramica di base su come creare componenti FAB adattabili al colore, reattivi e accessibili.

In questo post voglio condividere le mie idee su come creare componenti FAB adattabili al colore, adattabili e accessibili. Prova la demo e visualizza la fonte.

Se preferisci i video, ecco una versione di YouTube di questo post:

Panoramica

I FAB sono più comuni sui dispositivi mobili che sui computer desktop, ma sono prevalenti in entrambi gli scenari. Consentono di vedere le azioni principali e di renderle utili e onnipresenti. Questo stile dell'esperienza utente è stato reso famoso dalla UI di Material e i suoi suggerimenti per l'utilizzo e il posizionamento sono disponibili qui.

Elementi e stili

Il codice HTML per questi controlli comporta un elemento contenitore e un insieme di uno o più pulsanti. Il container posiziona i FAB all'interno dell'area visibile e gestisce uno spazio tra i pulsanti. I pulsanti possono essere mini o predefiniti, con una certa varietà di azioni principali e secondarie.

Contenitore FAB

Questo elemento può essere un normale <div>, ma facciamo un favore ai nostri utenti scettici e lo tagghiamo con alcuni attributi utili per spiegare lo scopo e i contenuti di questo contenitore.

Markup FAB

Inizia con una classe .fabs per il CSS a cui collegarti per lo stile, quindi aggiungi role="group" e aria-label in modo che non sia solo un container generico, ma abbia un nome e una finalità.

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

Stile dei FAB

Per praticità, i FAB rimangono sempre all'interno dell'area visibile. Questo è un ottimo caso d'uso per la posizione fixed. In questa posizione nell'area visibile ho scelto di utilizzare inset-block e inset-inline, in modo che la posizione si adatti alla modalità documento dell'utente, ad esempio da destra a sinistra o da sinistra a destra. Le proprietà personalizzate vengono utilizzate anche per evitare ripetizioni e garantire la stessa distanza dai bordi inferiore e laterale dell'area visibile:

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

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

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

Ora assegno il display del container flex e modifico la direzione del layout in column-reverse. In questo modo gli elementi secondari vengono sovrapposti (colonna) e ne inverte l'ordine visivo. In questo modo, il primo elemento attivabile diventa l'elemento inferiore anziché quello superiore, ovvero il punto in cui viene normalmente impostato lo stato attivo del documento HTML. L'inversione dell'ordine visivo unisce l'esperienza agli utenti vedenti e agli utenti che usano la tastiera, poiché lo stile dell'azione principale più grande dei mini pulsanti indica agli utenti vedenti che è un'azione principale e che gli utenti con tastiera la imposteranno come primo elemento nella sorgente.

Vengono visualizzati due pulsanti FAB con DevTools che si sovrappone al layout a griglia. Mostra lo spazio tra di loro con un motivo a righe e l&#39;altezza e la larghezza calcolate.

.fabs {
  …

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

La centratura viene gestita con place-items e gap aggiunge spazio tra qualsiasi pulsante FaB posizionato nel contenitore.

Pulsanti FAB

È ora di modificare lo stile di alcuni pulsanti in modo che appaiano sopra ogni elemento.

FAB predefinito

Il primo pulsante a cui applicare lo stile è quello predefinito. che fungerà da base per tutti i pulsanti FAB. Più avanti creeremo una variante in grado di ottenere un aspetto alternativo modificando il minor numero possibile di questi stili di base.

Markup FAB

L'elemento <button> è la scelta giusta. Inizieremo con questo modello di base, poiché offre un'ottima esperienza utente con mouse, tocco e tastiera. L'aspetto più cruciale di questo markup è nascondere l'icona agli utenti di screen reader con aria-hidden="true" e aggiungere il testo dell'etichetta necessario al markup <button> stesso. In questi casi, quando aggiungo etichette, mi piace anche aggiungere title in modo che gli utenti del mouse possano ottenere informazioni su ciò che l'icona spera di comunicare.

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

Stile FAB

Per prima cosa trasformiamo il pulsante in un pulsante rotondo imbottito con una marcata ombra, poiché queste sono le prime caratteristiche che lo definiscono:

.fab {
  --_size: 2rem;

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

Ora aggiungiamo il colore. Useremo una strategia che abbiamo già utilizzato nelle sfide GUI. Crea un insieme denominato chiaramente di proprietà personalizzate che contengono in modo statico i colori chiari e scuri, quindi una proprietà personalizzata adattiva che verrà impostata sulla variabile chiaro o scuro in base alla preferenza di sistema dell'utente per i colori:

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

Poi aggiungi alcuni stili per fare in modo che le icone SVG si adattino allo spazio.

.fab {
  …

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

Infine, rimuovi l'evidenziazione del tocco dal pulsante poiché abbiamo aggiunto un nostro feedback visivo per l'interazione:

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

Mini FAB

L'obiettivo di questa sezione è creare una variante del pulsante FAB. Riducendo alcuni FAB rispetto all'azione predefinita, possiamo promuovere l'azione che l'utente esegue più spesso.

Markup Mini FAB

L'HTML è uguale a un FAB, ma aggiungiamo una classe ".mini" per dare a CSS un hook nella 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>
Stile Mini FAB

Grazie all'utilizzo delle proprietà personalizzate, l'unica modifica necessaria è un aggiustamento alla variabile --_size.

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

Uno screenshot dei due pulsanti principali impilati e il pulsante in alto è più piccolo di quello in basso.

Accessibilità

La parte più importante da ricordare per l'accessibilità con i FAB è il posizionamento all'interno del flusso della tastiera della pagina. Questa demo contiene solo i FAB, non c'è nulla con cui competere in termini di ordine e flusso della tastiera, il che significa che non ha l'opportunità di dimostrare un flusso significativo della tastiera. In uno scenario in cui ci sono elementi in competizione per concentrarsi, ti consiglio di pensare attentamente a dove un utente si trova ad accedere al flusso dei pulsanti FAB.

Dimostrazione dell'interazione con la tastiera

Una volta che l'utente si è impostato con lo stato attivo sul contenitore FAB, abbiamo già aggiunto role="group" e aria-label="floating action buttons" che informano gli utenti degli screen reader dei contenuti su cui si sono concentrati. In modo strategico, ho posizionato per primo il FAB predefinito, in modo che gli utenti trovino per prima l'azione principale. Poi uso flex-direction: column-reverse; per ordinare visivamente il pulsante principale in basso, vicino alle dita degli utenti per accedervi facilmente. Questa è una bella vittoria perché il pulsante predefinito è ben visibile e anche il primo per chi usa la tastiera, garantendo un'esperienza molto simile.

Infine, non dimenticare di nascondere le tue icone agli utenti di screen reader e assicurati di fornire loro un'etichetta per il pulsante, in modo che non sia un mistero. Questa operazione è stata già eseguita nel codice HTML con aria-hidden="true" in <svg> e aria-label="Some action" in <button>.

Animazione

È possibile aggiungere vari tipi di animazione per migliorare l'esperienza utente. Come in altre GUI Challenge, imposteremo un paio di proprietà personalizzate per mantenere l'intento di un'esperienza di movimento ridotta e di un'esperienza di movimento completo. Per impostazione predefinita, gli stili presuppongono che l'utente voglia un movimento ridotto, quindi, utilizzando la query multimediale prefers-reduced-motion, scambia il valore di transizione con il movimento completo.

Una strategia di movimento ridotta con proprietà personalizzate

Nel seguente CSS vengono create tre proprietà personalizzate: --_motion-reduced, --_motion-ok e --_transition. Le prime due transizioni sono appropriate in base alle preferenze dell'utente e l'ultima variabile --_transition verrà impostata rispettivamente su --_motion-reduced o --_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);
  }
}

Con l'impostazione precedente, è possibile eseguire la transizione delle modifiche a box-shadow, background-color, transform e outline-offset, dando all'utente un feedback utile dell'interfaccia utente che indica che la sua interazione è stata ricevuta.

Poi, aggiungi un po' più di originalità allo stato :active regolando un po' translateYil pulsante, così da ottenere un bel effetto premuto:

.fab {
  …

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

Infine, trasferisci tutte le modifiche alle icone SVG nei pulsanti:

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

Conclusione

Ora che sai come ci sono riuscito, come faresti? 🙂

Diversifica i nostri approcci e scopriamo tutti i modi per creare sul web.

Crea una demo, inviami un tweet con i link e lo aggiungerò alla sezione Remix della community di seguito.

Remix della community

Ancora niente da visualizzare.

Risorse