Creazione di un componente Schede

Una panoramica di base su come creare un componente di schede simile a quelli presenti nelle app per iOS e Android.

In questo post voglio condividere alcune riflessioni sulla creazione di un componente Schede per il web che sia adattabile, supporti più input dei dispositivi e funzioni su tutti i browser. Prova la demo.

Demo

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

Panoramica

Le schede sono un componente comune dei sistemi di progettazione, ma possono assumere molte forme. Prima c'erano le schede per computer basate sull'elemento <frame>, ora abbiamo componenti mobili fluidi che animano i contenuti in base alle proprietà fisiche. Tutti cercano di fare la stessa cosa: risparmiare spazio.

Oggi, gli elementi essenziali di un'esperienza utente con schede sono un'area di navigazione con pulsanti che attiva/disattiva la visibilità dei contenuti in un frame di visualizzazione. Molte aree di contenuti diverse condividono lo stesso spazio, ma vengono presentate in modo condizionale in base al pulsante selezionato nella navigazione.

il collage è piuttosto caotico a causa dell'enorme diversità di stili che il web ha applicato al concetto di componente
Un collage di stili di web design dei componenti delle schede degli ultimi 10 anni

Tattiche per il web

Tutto sommato, ho trovato questo componente piuttosto semplice da creare, grazie ad alcune funzionalità critiche della piattaforma web:

  • scroll-snap-points per interazioni eleganti con scorrimento e tastiera con posizioni di arresto dello scorrimento appropriate
  • Deep link tramite hash degli URL per il supporto della condivisione e dell'ancoraggio dello scorrimento nella pagina gestito dal browser
  • Supporto per screen reader con markup degli elementi <a> e id="#hash"
  • prefers-reduced-motion per attivare le transizioni con dissolvenza incrociata e lo scorrimento istantaneo in-page
  • La funzionalità web in bozza @scroll-timeline per sottolineare e cambiare colore dinamicamente alla scheda selezionata

L'HTML

In sostanza, la UX è la seguente: fai clic su un link, l'URL rappresenta lo stato della pagina nidificata e poi l'area dei contenuti viene aggiornata mentre il browser scorre fino all'elemento corrispondente.

Sono presenti alcuni elementi di contenuto strutturale: link e :target. Abbiamo bisogno di un elenco di link, per cui un elemento <nav> è perfetto, e di un elenco di elementi <article>, per cui un elemento <section> è perfetto. Ogni hash del link corrisponderà a una sezione, consentendo al browser di scorrere gli elementi tramite l'ancoraggio.

Un pulsante di link viene selezionato, facendo scorrere i contenuti in primo piano

Ad esempio, se fai clic su un link, l'articolo :target viene attivato automaticamente in Chrome 89, senza bisogno di JS. L'utente può quindi scorrere i contenuti dell'articolo con il proprio dispositivo di input come sempre. Si tratta di contenuti complementari, come indicato nel markup.

Ho utilizzato il seguente markup per organizzare le schede:

<snap-tabs>
  <header>
    <nav>
      <a></a>
      <a></a>
      <a></a>
      <a></a>
    </nav>
  </header>
  <section>
    <article></article>
    <article></article>
    <article></article>
    <article></article>
  </section>
</snap-tabs>

Posso stabilire connessioni tra gli elementi <a> e <article> con le proprietà href e id in questo modo:

<snap-tabs>
  <header>
    <nav>
      <a href="#responsive"></a>
      <a href="#accessible"></a>
      <a href="#overscroll"></a>
      <a href="#more"></a>
    </nav>
  </header>
  <section>
    <article id="responsive"></article>
    <article id="accessible"></article>
    <article id="overscroll"></article>
    <article id="more"></article>
  </section>
</snap-tabs>

Poi ho riempito gli articoli con quantità variabili di testo segnaposto e i link con un insieme di titoli di lunghezza variabile e immagini. Ora che abbiamo dei contenuti con cui lavorare, possiamo iniziare con il layout.

Layout di scorrimento

In questo componente sono presenti tre diversi tipi di aree di scorrimento:

  • La navigazione (rosa) è scorrevole in orizzontale
  • L'area dei contenuti (blu) è scorrevole in orizzontale
  • Ogni elemento dell'articolo (verde) è scorrevole verticalmente.
3 caselle colorate con frecce direzionali di colore corrispondente che delineano le aree di scorrimento e mostrano la direzione in cui scorreranno.

Esistono due diversi tipi di elementi coinvolti nello scorrimento:

  1. Una finestra
    Una casella con dimensioni definite che ha lo stile della proprietà overflow.
  2. Una superficie di grandi dimensioni
    In questo layout, si tratta dei container degli elenchi: link di navigazione, articoli delle sezioni e contenuti degli articoli.

Layout <snap-tabs>

Il layout di primo livello che ho scelto è flex (Flexbox). Ho impostato la direzione su column, in modo che l'intestazione e la sezione siano ordinate verticalmente. Questa è la nostra prima finestra di scorrimento e nasconde tutto con overflow hidden. A breve, l'intestazione e la sezione utilizzeranno l'overscroll come zone singole.

HTML
<snap-tabs>
  <header></header>
  <section></section>
</snap-tabs>
CSS
  snap-tabs {
  display: flex;
  flex-direction: column;

  /* establish primary containing box */
  overflow: hidden;
  position: relative;

  & > section {
    /* be pushy about consuming all space */
    block-size: 100%;
  }

  & > header {
    /* defend against 
needing 100% */ flex-shrink: 0; /* fixes cross browser quarks */ min-block-size: fit-content; } }

Indicando di nuovo il diagramma colorato con tre pergamene:

  • <header> è ora pronto per essere il contenitore di scorrimento (rosa).
  • <section> è pronto per essere il container di scorrimento (blu).

I frame che ho evidenziato di seguito con VisBug ci aiutano a vedere le finestre create dai contenitori di scorrimento.

Gli elementi header e section hanno overlay rosa acceso che delineano lo spazio che occupano nel componente

Layout Schede <header>

Il layout successivo è quasi lo stesso: utilizzo flex per creare un ordine verticale.

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

La .snap-indicator deve spostarsi orizzontalmente con il gruppo di link e questo layout dell'intestazione aiuta a impostare questa fase. Nessun elemento con posizione assoluta qui.

Gli elementi nav e span.indicator hanno overlay rosa acceso che delineano lo spazio che occupano nel componente

Poi, gli stili di scorrimento. Si scopre che possiamo condividere gli stili di scorrimento tra le nostre due aree di scorrimento orizzontale (intestazione e sezione), quindi ho creato una classe di utilità, .scroll-snap-x.

.scroll-snap-x {
  /* browser decide if x is ok to scroll and show bars on, y hidden */
  overflow: auto hidden;
  /* prevent scroll chaining on x scroll */
  overscroll-behavior-x: contain;
  /* scrolling should snap children on x */
  scroll-snap-type: x mandatory;

  @media (hover: none) {
    scrollbar-width: none;

    &::-webkit-scrollbar {
      width: 0;
      height: 0;
    }
  }
}

Ognuno richiede l'overflow sull'asse x, il contenimento dello scorrimento per intercettare l'overscroll, barre di scorrimento nascoste per i dispositivi touch e, infine, l'allineamento dello scorrimento per bloccare le aree di presentazione dei contenuti. L'ordine di tabulazione della tastiera è accessibile e tutte le interazioni guidano lo stato attivo in modo naturale. I container di allineamento dello scorrimento ottengono anche una bella interazione in stile carosello dalla tastiera.

Layout intestazione schede <nav>

I link di navigazione devono essere disposti su una riga, senza interruzioni di riga, centrati verticalmente e ogni elemento del link deve essere agganciato al container di allineamento dello scorrimento. Ottimo lavoro per il CSS 2021!

HTML
<nav>
  <a></a>
  <a></a>
  <a></a>
  <a></a>
</nav>
CSS
  nav {
  display: flex;

  & a {
    scroll-snap-align: start;

    display: inline-flex;
    align-items: center;
    white-space: nowrap;
  }
}

Ogni link ha uno stile e una dimensione propri, quindi il layout di navigazione deve solo specificare la direzione e il flusso. Le larghezze uniche degli elementi di navigazione rendono divertente la transizione tra le schede, poiché l'indicatore regola la sua larghezza in base al nuovo target. A seconda del numero di elementi presenti, il browser mostrerà o meno una barra di scorrimento.

gli elementi a del nav hanno overlay rosa acceso, che delineano lo spazio che occupano nel componente e dove si verifica l'overflow

Layout Schede <section>

Questa sezione è un elemento flessibile e deve essere il principale consumatore di spazio. Deve anche creare colonne in cui inserire gli articoli. Ancora una volta, un lavoro rapido per CSS 2021. block-size: 100% estende questo elemento per riempire il più possibile l'elemento principale, quindi per il proprio layout crea una serie di colonne che corrispondono a 100% della larghezza dell'elemento principale. Le percentuali funzionano benissimo in questo caso perché abbiamo scritto vincoli rigidi per l'elemento principale.

HTML
<section>
  <article></article>
  <article></article>
  <article></article>
  <article></article>
</section>
CSS
  section {
  block-size: 100%;

  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
}

È come se dicessimo "espandi verticalmente il più possibile, in modo aggressivo" (ricorda l'intestazione che abbiamo impostato su flex-shrink: 0: è una difesa contro questa spinta di espansione), che imposta l'altezza della riga per un insieme di colonne ad altezza intera. Lo stile auto-flow indica alla griglia di disporre sempre gli elementi secondari in una riga orizzontale, senza ritorno a capo, esattamente ciò che vogliamo, ovvero l'overflow della finestra principale.

Gli elementi dell'articolo hanno overlay rosa acceso che delineano lo spazio che occupano nel componente e dove si verifica l'overflow.

A volte trovo difficile capire questi concetti. Questo elemento di sezione si inserisce in una casella, ma ha anche creato un insieme di caselle. Spero che le immagini e le spiegazioni siano utili.

Layout Schede <article>

L'utente deve essere in grado di scorrere i contenuti dell'articolo e le barre di scorrimento devono essere visualizzate solo se è presente un overflow. Questi elementi dell'articolo sono in una posizione ordinata. Sono contemporaneamente un elemento principale e un elemento secondario di scorrimento. Il browser gestisce alcune interazioni complesse con tocco, mouse e tastiera per noi.

HTML
<article>
  <h2></h2>
  <p></p>
  <p></p>
  <h2></h2>
  <p></p>
  <p></p>
  ...
</article>
CSS
article {
  scroll-snap-align: start;

  overflow-y: auto;
  overscroll-behavior-y: contain;
}

Ho scelto di agganciare gli articoli all'interno dello scroller principale. Mi piace molto il modo in cui gli elementi dei link di navigazione e gli elementi dell'articolo si agganciano all'inizio in linea dei rispettivi container di scorrimento. Sembra un rapporto armonioso.

L'elemento article e i relativi elementi secondari hanno overlay rosa acceso che delineano lo spazio che occupano nel componente e la direzione in cui si verifica l'overflow

L'articolo è un elemento secondario della griglia e le sue dimensioni sono predeterminate in modo da corrispondere all'area della viewport in cui vogliamo fornire l'esperienza utente di scorrimento. Ciò significa che non ho bisogno di stili di altezza o larghezza qui, devo solo definire come si verifica l'overflow. Imposto overflow-y su auto e poi intercetto anche le interazioni di scorrimento con la pratica proprietà overscroll-behavior.

Riepilogo di 3 aree di scorrimento

Di seguito ho scelto di "mostrare sempre le barre di scorrimento" nelle impostazioni di sistema. Penso che sia doppiamente importante che il layout funzioni con questa impostazione attiva, così come lo è per me rivedere il layout e l'orchestrazione dello scorrimento.

le tre barre di scorrimento sono impostate per essere visualizzate, occupando spazio nel layout, e il nostro componente ha ancora un bell'aspetto

Penso che vedere la barra di scorrimento in questo componente aiuti a mostrare chiaramente dove si trovano le aree di scorrimento, la direzione che supportano e come interagiscono tra loro. Considera come ciascuno di questi frame della finestra di scorrimento sia anche un elemento principale flex o grid di un layout.

DevTools può aiutarci a visualizzare questo aspetto:

Le aree di scorrimento hanno overlay degli strumenti griglia e flexbox, che delineano lo spazio che occupano nel componente e la direzione in cui si espandono.
Chromium Devtools, che mostra il layout dell'elemento nav flexbox pieno di elementi anchor, il layout della sezione della griglia pieno di elementi article e gli elementi article pieni di paragrafi e un elemento heading.

I layout di scorrimento sono completi: aggancio, deep link e accessibilità da tastiera. Una base solida per miglioramenti della UX, stile e soddisfazione.

Funzionalità in evidenza

Gli elementi secondari con allineamento dello scorrimento mantengono la loro posizione bloccata durante il ridimensionamento. Ciò significa che JavaScript non dovrà mostrare nulla quando il dispositivo viene ruotato o il browser viene ridimensionato. Provala nella modalità Dispositivo di Chromium DevTools selezionando una modalità diversa da Adattabile e ridimensionando il frame del dispositivo. Nota che l'elemento rimane visibile e bloccato con i relativi contenuti. Questa funzionalità è disponibile da quando Chromium ha aggiornato la sua implementazione per adattarla alla specifica. Ecco un blog post sull'argomento.

Animazione

L'obiettivo del lavoro di animazione qui è collegare chiaramente le interazioni con il feedback della UI. In questo modo, l'utente viene guidato o assistito nella scoperta (si spera) senza problemi di tutti i contenuti. Aggiungerò il movimento con uno scopo e in modo condizionale. Ora gli utenti possono specificare le loro preferenze di movimento nel sistema operativo e mi piace molto rispondere alle loro preferenze nelle mie interfacce.

Collegherò una sottolineatura della scheda alla posizione di scorrimento dell'articolo. Lo snapping non serve solo per un allineamento preciso, ma anche per ancorare l'inizio e la fine di un'animazione. In questo modo, la <nav>, che funge da mini mappa, rimane collegata ai contenuti. Controlleremo la preferenza di movimento dell'utente sia da CSS che da JS. Esistono alcune opzioni possibili.

Comportamento di scorrimento

È possibile migliorare il comportamento di movimento sia di :target che di element.scrollIntoView(). Per impostazione predefinita, è istantaneo. Il browser imposta semplicemente la posizione di scorrimento. E se volessimo passare a quella posizione di scorrimento, anziché arrivarci all'improvviso?

@media (prefers-reduced-motion: no-preference) {
  .scroll-snap-x {
    scroll-behavior: smooth;
  }
}

Poiché qui stiamo introducendo il movimento, e un movimento che l'utente non controlla (come lo scorrimento), applichiamo questo stile solo se l'utente non ha preferenze nel proprio sistema operativo in merito al movimento ridotto. In questo modo, introduciamo il movimento di scorrimento solo per gli utenti che non hanno problemi con questo tipo di movimento.

Indicatore delle schede

Lo scopo di questa animazione è aiutare ad associare l'indicatore allo stato dei contenuti. Ho deciso di applicare la dissolvenza incrociata dei colori agli stili border-bottom per gli utenti che preferiscono un movimento ridotto e un'animazione di scorrimento con dissolvenza dei colori per gli utenti che non hanno problemi con il movimento.

In Chromium Devtools, posso attivare/disattivare la preferenza e mostrare i due diversi stili di transizione. Mi sono divertito molto a creare questo progetto.

@media (prefers-reduced-motion: reduce) {
  snap-tabs > header a {
    border-block-end: var(--indicator-size) solid hsl(var(--accent) / 0%);
    transition: color .7s ease, border-color .5s ease;

    &:is(:target,:active,[active]) {
      color: var(--text-active-color);
      border-block-end-color: hsl(var(--accent));
    }
  }

  snap-tabs .snap-indicator {
    visibility: hidden;
  }
}

Nascondo l'elemento .snap-indicator quando l'utente preferisce un movimento ridotto, poiché non ne ho più bisogno. Poi lo sostituisco con gli stili border-block-end e un transition. Notate anche nell'interazione delle schede che l'elemento di navigazione attivo non solo ha una sottolineatura del brand, ma anche il colore del testo è più scuro. L'elemento attivo ha un contrasto del colore del testo più elevato e un accento luminoso sotto la luce.

Bastano poche righe di CSS in più per far sentire qualcuno visto (nel senso che rispettiamo con attenzione le sue preferenze di movimento). Mi piace un sacco.

@scroll-timeline

Nella sezione precedente ho mostrato come gestisco gli stili di dissolvenza incrociata con movimento ridotto, mentre in questa sezione mostrerò come ho collegato l'indicatore e un'area di scorrimento. Ora passiamo a qualcosa di divertente e sperimentale. Spero che tu sia entusiasta quanto me.

const { matches:motionOK } = window.matchMedia(
  '(prefers-reduced-motion: no-preference)'
);

Per prima cosa, controllo la preferenza di movimento dell'utente da JavaScript. Se il risultato è false, ovvero l'utente preferisce un movimento ridotto, non eseguiremo nessuno degli effetti di movimento di collegamento allo scorrimento.

if (motionOK) {
  // motion based animation code
}

Al momento della stesura di questo articolo, il supporto dei browser per @scroll-timeline è nullo. Si tratta di una bozza di specifica con solo implementazioni sperimentali. Tuttavia, ha un polyfill, che utilizzo in questa demo.

ScrollTimeline

Sebbene sia CSS che JavaScript possano creare cronologie di scorrimento, ho scelto JavaScript per poter utilizzare le misurazioni degli elementi in tempo reale nell'animazione.

const sectionScrollTimeline = new ScrollTimeline({
  scrollSource: tabsection,  // snap-tabs > section
  orientation: 'inline',     // scroll in the direction letters flow
  fill: 'both',              // bi-directional linking
});

Voglio che un elemento segua la posizione di scorrimento di un altro e, creando un ScrollTimeline, definisco il driver del link di scorrimento, ovvero scrollSource. In genere, un'animazione sul web viene eseguita in base a un tick di intervallo di tempo globale, ma con un sectionScrollTimeline personalizzato in memoria, posso cambiare tutto questo.

tabindicator.animate({
    transform: ...,
    width: ...,
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

Prima di passare ai keyframe dell'animazione, penso sia importante sottolineare che l'elemento che segue lo scorrimento, tabindicator, verrà animato in base a una sequenza temporale personalizzata, ovvero lo scorrimento della nostra sezione. In questo modo il collegamento è completo, ma manca l'ingrediente finale, ovvero i punti stateful tra cui eseguire l'animazione, noti anche come keyframe.

Fotogrammi chiave dinamici

Esiste un modo CSS dichiarativo puro molto efficace per animare con @scroll-timeline, ma l'animazione che ho scelto di realizzare era troppo dinamica. Non è possibile passare tra la larghezza auto e non è possibile creare dinamicamente un numero di keyframe in base alla lunghezza degli elementi secondari.

Tuttavia, JavaScript sa come ottenere queste informazioni, quindi eseguiremo l'iterazione sugli elementi secondari e acquisiremo i valori calcolati in fase di runtime:

tabindicator.animate({
    transform: [...tabnavitems].map(({offsetLeft}) =>
      `translateX(${offsetLeft}px)`),
    width: [...tabnavitems].map(({offsetWidth}) =>
      `${offsetWidth}px`)
  }, {
    duration: 1000,
    fill: 'both',
    timeline: sectionScrollTimeline,
  }
);

Per ogni tabnavitem, destruttura la posizione offsetLeft e restituisci una stringa che la utilizza come valore translateX. In questo modo vengono creati 4 fotogrammi chiave di trasformazione per l'animazione. Lo stesso vale per la larghezza: a ogni elemento viene chiesto qual è la sua larghezza dinamica, che viene poi utilizzata come valore del keyframe.

Ecco un esempio di output, basato sui miei caratteri e sulle mie preferenze del browser:

TranslateX Keyframes:

[...tabnavitems].map(({offsetLeft}) =>
  `translateX(${offsetLeft}px)`)

// results in 4 array items, which represent 4 keyframe states
// ["translateX(0px)", "translateX(121px)", "translateX(238px)", "translateX(464px)"]

Fotogrammi chiave della larghezza:

[...tabnavitems].map(({offsetWidth}) =>
  `${offsetWidth}px`)

// results in 4 array items, which represent 4 keyframe states
// ["121px", "117px", "226px", "67px"]

Per riassumere la strategia, l'indicatore della scheda ora si animerà in 4 keyframe a seconda della posizione di allineamento dello scorrimento dello scroller della sezione. I punti di ancoraggio creano una chiara delimitazione tra i fotogrammi chiave e contribuiscono davvero alla sensazione di sincronizzazione dell'animazione.

La scheda attiva e la scheda non attiva vengono mostrate con overlay VisBug che mostrano punteggi di contrasto positivi per entrambe

L'utente guida l'animazione con la sua interazione, vedendo la larghezza e la posizione dell'indicatore cambiare da una sezione all'altra, monitorando perfettamente lo scorrimento.

Forse non l'avrai notato, ma sono molto orgoglioso della transizione di colore quando l'elemento di navigazione evidenziato viene selezionato.

Il grigio più chiaro non selezionato appare ancora più arretrato quando l'elemento evidenziato ha un contrasto maggiore. È comune applicare una transizione di colore al testo, ad esempio al passaggio del mouse e quando viene selezionato, ma è un livello superiore applicare una transizione di colore allo scorrimento, sincronizzata con l'indicatore di sottolineatura.

Ecco come ho fatto:

tabnavitems.forEach(navitem => {
  navitem.animate({
      color: [...tabnavitems].map(item =>
        item === navitem
          ? `var(--text-active-color)`
          : `var(--text-color)`)
    }, {
      duration: 1000,
      fill: 'both',
      timeline: sectionScrollTimeline,
    }
  );
});

Ogni link di navigazione delle schede deve avere questa nuova animazione del colore, che segue la stessa cronologia di scorrimento dell'indicatore di sottolineatura. Utilizzo la stessa cronologia di prima: poiché il suo ruolo è quello di emettere un tick durante lo scorrimento, possiamo utilizzarlo in qualsiasi tipo di animazione che vogliamo. Come ho fatto prima, creo 4 keyframe nel loop e restituisco i colori.

[...tabnavitems].map(item =>
  item === navitem
    ? `var(--text-active-color)`
    : `var(--text-color)`)

// results in 4 array items, which represent 4 keyframe states
// [
  "var(--text-active-color)",
  "var(--text-color)",
  "var(--text-color)",
  "var(--text-color)",
]

Il keyframe con il colore var(--text-active-color) evidenzia il link, mentre per il resto è un colore di testo standard. Il ciclo nidificato lo rende relativamente semplice, poiché il ciclo esterno è ogni elemento di navigazione e il ciclo interno è i keyframe personali di ogni elemento di navigazione. Controllo se l'elemento del loop esterno è lo stesso di quello del loop interno e lo utilizzo per sapere quando è selezionato.

Mi sono divertito molto a scrivere questo articolo. Tantissimo.

Ancora più miglioramenti di JavaScript

Vale la pena ricordare che il nucleo di ciò che ti mostro qui funziona senza JavaScript. Detto questo, vediamo come possiamo migliorarla quando JS è disponibile.

I deep link sono un termine più mobile, ma penso che l'intento del deep link sia soddisfatto qui con le schede, in quanto puoi condividere un URL direttamente ai contenuti di una scheda. Il browser eseguirà la navigazione all'interno della pagina fino all'ID corrispondente nell'hash dell'URL. Ho scoperto che questo gestore onload ha creato l'effetto su più piattaforme.

window.onload = () => {
  if (location.hash) {
    tabsection.scrollLeft = document
      .querySelector(location.hash)
      .offsetLeft;
  }
}

Sincronizzazione della fine dello scorrimento

I nostri utenti non fanno sempre clic o non usano sempre una tastiera, a volte scorrono semplicemente, come dovrebbero poter fare. Quando lo scroller della sezione interrompe lo scorrimento, la posizione in cui si ferma deve corrispondere alla barra di navigazione in alto.

Ecco come attendo la fine dello scorrimento: js tabsection.addEventListener('scroll', () => { clearTimeout(tabsection.scrollEndTimer); tabsection.scrollEndTimer = setTimeout(determineActiveTabSection, 100); });

Ogni volta che si scorrono le sezioni, cancella il timeout della sezione, se presente, e avviane uno nuovo. Quando lo scorrimento delle sezioni si interrompe, non cancellare il timeout e attiva l'evento 100 ms dopo l'interruzione. Quando viene attivato, chiama la funzione che cerca di capire dove l'utente si è fermato.

const determineActiveTabSection = () => {
  const i = tabsection.scrollLeft / tabsection.clientWidth;
  const matchingNavItem = tabnavitems[i];

  matchingNavItem && setActiveTab(matchingNavItem);
};

Supponendo che l'allineamento dello scorrimento sia stato applicato, la divisione della posizione di scorrimento corrente per la larghezza dell'area di scorrimento dovrebbe restituire un numero intero e non un decimale. Poi provo a recuperare un elemento di navigazione dalla nostra cache tramite questo indice calcolato e, se trova qualcosa, invio la corrispondenza da impostare come attiva.

const setActiveTab = tabbtn => {
  tabnav
    .querySelector(':scope a[active]')
    .removeAttribute('active');

  tabbtn.setAttribute('active', '');
  tabbtn.scrollIntoView();
};

L'impostazione della scheda attiva inizia con la cancellazione di qualsiasi scheda attualmente attiva, quindi l'assegnazione dell'attributo di stato attivo all'elemento di navigazione in entrata. La chiamata a scrollIntoView() ha un'interazione divertente con CSS che vale la pena notare.

.scroll-snap-x {
  overflow: auto hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;

  @media (prefers-reduced-motion: no-preference) {
    scroll-behavior: smooth;
  }
}

Nel CSS dell'utilità di allineamento dello scorrimento orizzontale, abbiamo nidificato una query supporti che applica lo scorrimento smooth se l'utente tollera il movimento. JavaScript può effettuare liberamente chiamate per scorrere gli elementi nella visualizzazione e CSS può gestire la UX in modo dichiarativo. A volte formano una coppia davvero deliziosa.

Conclusione

Ora che sai come ho fatto io, come faresti tu? Questo rende l'architettura dei componenti divertente. Chi realizzerà la prima versione con gli slot nel proprio framework preferito? 🙂

Diversifichiamo i nostri approcci e impariamo tutti i modi per creare sul web. Crea un Glitch, twittami la tua versione e la aggiungerò alla sezione Remix della community di seguito.

Remix della community