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

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

Panoramica

Le 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, offrendo 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 intenzionale.

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

Stile delle FAB

Per essere pratici, i pulsanti flottanti rimangono sempre nell'area visibile. Questo è un ottimo caso d'uso per la posizione fixed. In questa posizione del viewport ho scelto di utilizzare inset-block e inset-inline in modo che la posizione sia in linea con la modalità del 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 laterali della visualizzazione:

.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 viene invertito anche il loro ordine visivo. 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 dei mini pulsanti e indica agli utenti vedenti che si tratta di un'azione principale, mentre 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 la distanza tra di loro con un motivo a strisce e mostra anche l&#39;altezza e la larghezza calcolate.

.fabs {
  

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

Il centratura viene gestita con place-items e gap aggiunge spazio tra i pulsanti FAB posizionati nel contenitore.

Pulsanti FAB

È il momento di dare stile ad alcuni pulsanti in modo che sembrino galleggiare sopra a tutto.

Pulsante flottante predefinito

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

Markup FAB

L'elemento <button> è la scelta giusta. Inizieremo da qui come base perché offre un'esperienza utente ottimale con mouse, tocco e tastiera. L'aspetto più importante 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 una forte ombra, 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);
  }
}

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 dell'azione predefinita, possiamo promuovere l'azione che l'utente esegue più spesso.

Markup mini FAB

Il codice HTML è lo stesso di un FAB, ma aggiungiamo una classe ".mini" per fornire al CSS un punto di contatto con 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>
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 contiene 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 un scenario in cui sono presenti elementi in competizione per l'attenzione, ti consiglio di riflettere attentamente su dove in quel flusso un utente dovrebbe trovarsi a entrare nel flusso del pulsante FAB.

Demonstrazione 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" per <svg> e aria-label="Some action" per i <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, esegui la transizione di 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 ho fatto, come faresti? 🙂

Diversifichiamo 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