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 opinioni su come creare componenti FAB adattabili, reattivi e accessibili. Prova la demo e visualizza il codice sorgente.

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

Panoramica

I FAB sono più comuni sui dispositivi mobili che sui computer, ma sono prevalenti in entrambi gli scenari. Mantengono visibili le azioni principali, rendendole comode e omnipresenti. Questo stile di esperienza utente è stato reso famoso da Material UI e i suoi suggerimenti per l'utilizzo e il posizionamento sono disponibili qui.

Elementi e stili

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

Contenitore FAB

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

Markup delle FAB

Inizia con una classe .fabs a cui il CSS può collegarsi per lo stile, quindi aggiungi role="group" e aria-label in modo che non sia solo un contenitore generico, ma sia nominato e utile.

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

Stile delle FAB

Per comodità, i FAB rimangono sempre all'interno dell'area visibile. Questo è un ottimo caso d'uso per la posizione fixed. All'interno di questa posizione dell'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 le 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);
}

Poi associo al contenitore il display flex e ne modifico la direzione del layout in column-reverse. In questo modo, gli elementi secondari vengono impilati uno sopra l'altro (colonna) e anche il loro ordine visivo viene invertito. Questo ha l'effetto di rendere il primo elemento attivabile l'elemento inferiore anziché quello superiore, che è dove lo stato attivo viene normalmente impostato nel documento HTML. L'inversione dell'ordine visivo unisce l'esperienza per gli utenti vedenti e quelli che usano la tastiera, poiché lo stile dell'azione principale più grande rispetto ai mini pulsanti indica agli utenti vedenti che si tratta di un'azione principale e gli utenti che usano la tastiera la metteranno a fuoco come primo elemento nell'origine.

Sono visualizzati due pulsanti FAB con DevTools sovrapposti al layout della griglia. Mostra lo spazio tra di loro con un motivo a strisce e mostra anche altezza e 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

È il momento di applicare uno stile ad alcuni pulsanti in modo che risultino fluttuanti sopra ogni cosa.

FAB predefinito

Il primo pulsante da applicare è il pulsante predefinito. che fungerà da base per tutti i pulsanti FAB. In seguito, creeremo una variante che raggiunge un aspetto alternativo modificando il minor numero possibile di questi stili di base.

Markup FAB

L'elemento <button> è la scelta giusta. Inizieremo con questa base, perché offre un'ottima esperienza utente con mouse, tocco ed esperienza della tastiera. L'aspetto più fondamentale 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. Quando aggiungi etichette in questi casi, mi piace anche aggiungere un title in modo che gli utenti che usano il mouse possano ottenere informazioni su ciò che l'icona vuole 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 arrotondato con bordi rialzati e un'ombra marcata, poiché si tratta delle 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);
}

Aggiungiamo il colore. Utilizzeremo una strategia già utilizzata in precedenza nelle sfide GUI. Crea un insieme di proprietà personalizzate denominate in modo chiaro che contengono in modo statico i colori chiari e scuri, quindi una proprietà personalizzata adattiva che verrà impostata sulle variabili chiare o scure a seconda della 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);
  }
}

Successivamente, aggiungi alcuni stili per adattare le icone SVG 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 il nostro feedback visivo per l'interazione:

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

Mini FAB

Lo scopo di questa sezione è creare una variante per il pulsante FAB. Rendendo alcuni FAB più piccoli rispetto all'azione predefinita, possiamo promuovere l'azione più spesso eseguita dall'utente.

Markup Mini FAB

L'HTML è uguale al FAB, ma aggiungiamo una classe ".mini" per assegnare al 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 FAB mini

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

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

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

Accessibilità

L'aspetto più importante da ricordare per l'accessibilità con i FAB è il posizionamento all'interno del flusso della tastiera della pagina. Questa demo ha solo i FAB, non c'è concorrenza in termini di ordine e flusso della tastiera, il che significa che non ha l'opportunità di dimostrare un flusso della tastiera significativo. In uno scenario in cui ci sono elementi concorrenti per l'attenzione, suggerisco di pensare attentamente a dove un utente dovrebbe trovarsi in questo flusso nel flusso del pulsante FAB.

Dimostrazione dell'interazione con la tastiera

Una volta che l'utente ha attivato il contenitore FAB, abbiamo già aggiunto role="group" e aria-label="floating action buttons" per informare gli utenti dello screen reader sui contenuti su cui hanno attivato la funzionalità. Per motivi strategici, ho collocato prima il FAB predefinito, in modo che gli utenti trovino prima l'azione principale. Poi uso flex-direction: column-reverse; per disporre visivamente il pulsante principale in basso, vicino alle dita dell'utente per un facile accesso. Si tratta di un risultato positivo perché il pulsante predefinito è in evidenza e viene visualizzato per primo per gli utenti con tastiera, offrendo loro esperienze molto simili.

Infine, non dimenticare di nascondere le icone agli utenti che utilizzano screen reader e assicurati di fornire loro un'etichetta per il pulsante in modo che non sia un mistero. Questo è stato già fatto in HTML con aria-hidden="true" su <svg> e aria-label="Some action" sui <button>.

Animazione

È possibile aggiungere vari tipi di animazioni per migliorare l'esperienza utente. Come in altre sfide GUI, configureremo un paio di proprietà personalizzate per contenere lo scopo di un'esperienza con movimento ridotto e un'esperienza con movimento completo. Per impostazione predefinita, gli stili presuppongono che l'utente voglia ridurre il movimento, quindi utilizzando la query media prefers-reduced-motion scambia il valore di transizione con il movimento completo.

Una strategia di movimento ridotto con proprietà personalizzate

Nel seguente CSS vengono create tre proprietà personalizzate: --_motion-reduced, --_motion-ok e --_transition. Le prime due contengono le transizioni 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 quanto sopra, è possibile eseguire la transizione delle modifiche a box-shadow, background-color, transform e outline-offset, fornendo all'utente un feedback sull'interfaccia utente che indica che la sua interazione è stata ricevuta.

Aggiungi un po' di stile allo stato :active modificando leggermente translateY, in modo da dare al pulsante un bell'effetto di pressione:

.fab {
  

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

Infine, passa eventuali 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 ho fatto, come faresti‽ 🙂

Diversificaamo i nostri approcci e impariamo tutti i modi per creare sul web.

Crea una demo, twittami i link e io la aggiungerò alla sezione dei remix della community di seguito.

Remix della community

Ancora nessun elemento da visualizzare.

Risorse