Creazione di un componente Schede

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

In questo post voglio condividere il mio pensiero sulla creazione di un componente Schede per il web che sia reattivo, supporti più input da 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 e modalità. Prima c'erano le schede per computer basate sull'elemento <frame> e ora abbiamo componenti per dispositivi 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 a 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&#39;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 web

Nel complesso, 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
  • Link diretti tramite hash URL per supporto della condivisione e dell'ancoraggio dello scorrimento nella pagina gestito dal browser
  • Supporto dello screen reader con markup degli elementi <a> e id="#hash"
  • prefers-reduced-motion per attivare le transizioni crossfade e lo scorrimento istantaneo nella pagina
  • La funzionalità web @scroll-timeline in bozza per sottolineare e cambiare dinamicamente il colore della scheda selezionata

L'HTML

Fondamentalmente, l'esperienza utente qui è: fai clic su un link, l'URL rappresenta lo stato della pagina nidificata e poi l'area dei contenuti si aggiorna man mano che il browser scorre fino all'elemento corrispondente.

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

Si fa clic su un pulsante di collegamento, che fa scorrere i contenuti in primo piano

Ad esempio, se fai clic su un link, l'articolo :target viene messo automaticamente in evidenza in Chrome 89, senza bisogno di JS. L'utente può quindi scorrere i contenuti dell'articolo con il dispositivo di input come sempre. Si tratta di contenuti senza costi, 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>

Successivamente ho riempito gli articoli con quantità miste di lorem e i link con una lunghezza mista e un set di titoli di immagini. Con i contenuti da utilizzare, possiamo iniziare il layout.

Layout scorrevoli

Questo componente include tre diversi tipi di aree di scorrimento:

  • La navigazione (rosa) è scorrevole orizzontalmente
  • L'area dei contenuti (blu) è scorrevole orizzontalmente
  • Ogni elemento dell'articolo (verde) è scorrevole verticalmente.
Tre caselle colorate con frecce direzionali che corrispondono ai colori delle aree di scorrimento e mostrano la direzione di scorrimento.

Esistono due tipi diversi di elementi coinvolti nello scorrimento:

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

Layout <snap-tabs>

Il layout di primo livello che ho scelto è flex (Flexbox). Ho impostato la direzione su column, quindi l'intestazione e la sezione sono ordinate verticalmente. Questa è la nostra prima finestra di scorrimento e nasconde tutto ciò che ha un overflow nascosto. A breve, l'intestazione e la sezione utilizzeranno l'overscroll come zone individuali.

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

Tornando al diagramma colorato a tre scorrimenti:

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

I frame che ho evidenziato di seguito con VisBug ci aiutano a vedere le finestre che i contenitori di scorrimento hanno creato.

gli elementi di intestazione e sezione hanno overlay rosa acceso che delineano lo spazio che occupano nel componente

Layout <header> delle schede

Il layout successivo è quasi identico: 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;
}

Il .snap-indicator deve spostarsi orizzontalmente con il gruppo di link e questo layout dell'intestazione aiuta a preparare il terreno. Nessun elemento con posizionamento assoluto qui.

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

Poi, gli stili di scorrimento. Abbiamo scoperto che possiamo condividere gli stili di scorrimento tra le due aree di scorrimento orizzontale (intestazione e sezione), quindi ho creato una classe utilitaria, .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 deve avere overflow sull'asse x, contenimento dello scorrimento per bloccare l'overscroll, barre di scorrimento nascoste per i dispositivi touch e infine scorrimento rapido per bloccare le aree di presentazione dei contenuti. L'ordine delle schede della tastiera è accessibile e qualsiasi guida alle interazioni si concentra in modo naturale. I contenitori di scorrimento rapido ottengono anche una piacevole interazione in stile carosello dalla tastiera.

Layout dell'intestazione delle schede <nav>

I link di navigazione devono essere disposti in una riga, senza interruzioni di riga, centrati verticalmente e ogni elemento di link deve essere allineato al contenitore di scorrimento rapido. Swift work for 2021 CSS!

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 si adatta a stili e dimensioni, quindi il layout di navigazione deve specificare solo 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 visualizzerà o meno una barra di scorrimento.

Gli elementi a della navigazione hanno overlay rosa acceso, che delineano lo spazio che occupano nel componente e dove si verifica l&#39;overflow

Layout <section> delle schede

Questa sezione è un elemento flessibile e deve occupare la maggior parte dello 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 contenitore il più possibile, quindi per il proprio layout crea una serie di colonne che sono 100% la larghezza del contenitore. Le percentuali funzionano benissimo qui 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 espansione), che imposta l'altezza della riga per un insieme di colonne a tutta altezza. Lo stile auto-flow indica alla griglia di disporre sempre gli elementi secondari in una riga orizzontale, senza wrapping, esattamente come vogliamo, in modo che superino i limiti della finestra principale.

Gli elementi dell&#39;articolo hanno overlay rosa acceso che delineano lo spazio che occupano nel componente e dove si verifica l&#39;overflow

A volte faccio fatica a capirli. Questo elemento della sezione si inserisce in una casella, ma ha anche creato un insieme di caselle. Spero che le immagini e le spiegazioni ti siano utili.

Layout <article> delle schede

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

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 fare in modo che gli articoli si adattassero allo scorrimento della pagina principale. Mi piace molto il modo in cui gli elementi del link di navigazione e gli elementi dell'articolo si agganciano all'inizio in linea dei rispettivi contenitori di scorrimento. Sembra una relazione armoniosa.

L&#39;elemento articolo 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&#39;overflow

L'articolo è un elemento secondario della griglia e le sue dimensioni sono predeterminate per essere l'area del riquadro di visualizzazione 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. Ho impostato overflow-y su auto e ho anche intercettato le interazioni di scorrimento con la pratica proprietà overscroll-behavior.

Riepilogo delle tre aree di scorrimento

Di seguito ho scelto nelle impostazioni di sistema di "mostrare sempre le barre di scorrimento". Penso che sia ancora più importante che il layout funzioni con questa impostazione attivata, in quanto mi consente di rivedere il layout e l'orchestrazione dello scorrimento.

le tre barre di scorrimento sono impostate per essere visualizzate, ora occupano spazio nel layout e il nostro componente ha ancora un bell&#39;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 flessibile o a griglia per 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 verifica l&#39;overflow
Chromium Devtools, che mostra il layout dell'elemento di navigazione flexbox pieno di elementi di ancoraggio, il layout della sezione della griglia pieno di elementi articolo e gli elementi articolo pieni di paragrafi e un elemento di intestazione.

I layout di scorrimento sono completi: snapping, link diretti e accessibilità da tastiera. Una base solida per i miglioramenti dell'esperienza utente, dello stile e del piacere.

Funzionalità in evidenza

Gli elementi secondari con scorrimento mantengono la posizione bloccata durante il ridimensionamento. Ciò significa che JavaScript non dovrà portare nulla in visualizzazione quando il dispositivo viene ruotato o la finestra del browser viene ridimensionata. Prova la modalità Dispositivo in Chromium DevTools selezionando una modalità diversa da Responsive e ridimensionando il frame del dispositivo. Nota che l'elemento rimane in visualizzazione e bloccato con i suoi contenuti. Questa funzionalità è disponibile da quando Chromium ha aggiornato la sua implementazione in modo che corrisponda alla specifica. Ecco un post del blog al riguardo.

Animazione

L'obiettivo del lavoro di animazione qui è collegare chiaramente le interazioni con il feedback dell'interfaccia utente. In questo modo, l'utente viene guidato o assistito nella scoperta (si spera) senza problemi di tutti i contenuti. Aggiungerò movimento con uno scopo e in modo condizionale. Ora gli utenti possono specificare le proprie 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. L'allineamento non è solo una questione estetica, ma anche un modo per ancorare l'inizio e la fine di un'animazione. In questo modo, <nav>, che funge da mini-mappa, rimane collegato ai contenuti. Controlleremo la preferenza di movimento dell'utente sia da CSS che da JS. Ci sono alcuni ottimi posti in cui essere rispettosi.

Comportamento di scorrimento

Esiste l'opportunità di migliorare il comportamento di movimento sia di :target sia di element.scrollIntoView(). Per impostazione predefinita, è immediato. Il browser imposta solo la posizione di scorrimento. E se volessimo passare a quella posizione di scorrimento, invece di lampeggiare lì?

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

Poiché qui introduciamo il movimento, e un movimento che l'utente non controlla (come lo scorrimento), applichiamo questo stile solo se l'utente non ha preferenze nel sistema operativo in merito al movimento ridotto. In questo modo, introduciamo il movimento di scorrimento solo per le persone che lo accettano.

Indicatore delle schede

Lo scopo di questa animazione è aiutare ad associare l'indicatore allo stato dei contenuti. Ho deciso di applicare un crossfade di colore agli stili border-bottom per gli utenti che preferiscono un movimento ridotto e un'animazione di scorrimento collegata allo scorrimento e dissolvenza del colore 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 tantissimo a realizzarlo.

@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 .snap-indicator quando l'utente preferisce un movimento ridotto, perché non mi serve più. Poi lo sostituisco con gli stili border-block-end e un transition. Inoltre, nell'interazione con le schede, l'elemento di navigazione attivo non solo ha un'evidenziazione sottolineata 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 di illuminazione inferiore brillante.

Basta qualche riga di CSS in più per far sentire una persona vista (nel senso che rispettiamo attentamente le sue preferenze di movimento). Mi piace un sacco.

@scroll-timeline

Nella sezione precedente ti ho mostrato come gestisco gli stili di dissolvenza incrociata con movimento ridotto e in questa sezione ti mostrerò come ho collegato l'indicatore e un'area di scorrimento. A seguire, alcune funzionalità sperimentali divertenti. 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 il movimento ridotto, non verranno eseguiti gli effetti di movimento del collegamento di scorrimento.

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

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

ScrollTimeline

Anche se CSS e JavaScript possono creare timeline 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 collegamento di scorrimento, il scrollSource. Normalmente un'animazione sul web viene eseguita in base a un intervallo di tempo globale, ma con un sectionScrollTimeline personalizzato in memoria, posso cambiare tutto.

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

Prima di entrare nel dettaglio dei fotogrammi chiave dell'animazione, penso sia importante sottolineare che il cursore di scorrimento, tabindicator, verrà animato in base a una sequenza temporale personalizzata, lo scorrimento della sezione. Il collegamento è completato, ma manca l'ingrediente finale, ovvero i punti stateful tra cui animare, noti anche come fotogrammi chiave.

Fotogrammi chiave dinamici

Esiste un modo CSS dichiarativo puro molto efficace per creare animazioni con @scroll-timeline, ma l'animazione che ho scelto di fare era troppo dinamica. Non è possibile eseguire la transizione tra la larghezza di auto e non è possibile creare dinamicamente un numero di fotogrammi chiave in base alla lunghezza dei figli.

JavaScript sa come ottenere queste informazioni, quindi itereremo i figli e recupereremo 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 e poi viene utilizzata come valore del fotogramma chiave.

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

Fotogrammi chiave TranslateX:

[...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 anima su 4 fotogrammi chiave a seconda della posizione di scorrimento della sezione. I punti di snap creano una chiara distinzione tra i fotogrammi chiave e contribuiscono a dare un aspetto sincronizzato all'animazione.

La scheda attiva e quella inattiva vengono mostrate con le sovrapposizioni di VisBug, che mostrano i punteggi di contrasto superati 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, seguendo perfettamente lo scorrimento.

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

Il grigio chiaro non selezionato appare ancora più in secondo piano quando l'elemento evidenziato ha un contrasto maggiore. È comune eseguire la transizione del colore per il testo, ad esempio al passaggio del mouse e quando viene selezionato, ma è un livello successivo eseguire la transizione del colore durante lo scorrimento, in sincronia 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 a schede deve avere questa nuova animazione di colore, che segue la stessa sequenza temporale di scorrimento dell'indicatore di sottolineatura. Utilizzo la stessa sequenza temporale di prima: poiché il suo ruolo è quello di emettere un segno di spunta durante lo scorrimento, possiamo utilizzarlo in qualsiasi tipo di animazione che vogliamo. Come ho fatto prima, creo 4 fotogrammi chiave nel ciclo 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 fotogramma chiave con il colore var(--text-active-color) evidenzia il link, mentre il colore del testo è standard. Il ciclo nidificato rende il tutto relativamente semplice, in quanto il ciclo esterno è ogni elemento di navigazione e il ciclo interno è ogni fotogramma chiave personale dell'elemento di navigazione. Controllo se l'elemento del ciclo esterno è uguale a quello del ciclo interno e lo utilizzo per sapere quando è selezionato.

Mi sono divertito molto a scriverla. Tantissimo.

Ancora più miglioramenti di JavaScript

È importante ricordare che la base di ciò che ti mostro qui funziona senza JavaScript. Detto questo, vediamo come possiamo migliorarlo quando JS è disponibile.

I link diretti sono più un termine mobile, ma credo che l'intento del link diretto sia soddisfatto qui con le schede, in quanto puoi condividere un URL direttamente ai contenuti di una scheda. Il browser passerà alla navigazione in pagina all'ID corrispondente nell'hash dell'URL. Ho trovato questo onload gestore che ha creato l'effetto su tutte le piattaforme.

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

Sincronizzazione della fine dello scorrimento

I nostri utenti non sempre fanno clic o usano una tastiera, a volte scorrono liberamente, come dovrebbero poter fare. Quando lo scorrimento della sezione si interrompe, la sezione in cui si ferma deve corrispondere a quella della 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 le sezioni vengono scorrete, cancella il timeout della sezione, se presente, e avviane uno nuovo. Quando le sezioni smettono di scorrere, non cancellare il timeout e attiva l'evento 100 ms dopo l'arresto. Quando viene attivato, chiama la funzione che cerca di capire dove si è interrotto l'utente.

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

  matchingNavItem && setActiveTab(matchingNavItem);
};

Supponendo che lo scorrimento sia stato eseguito correttamente, la divisione della posizione di scorrimento corrente per la larghezza dell'area di scorrimento deve restituire un numero intero e non 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 viene assegnato all'elemento di navigazione in entrata l'attributo di stato attivo. La chiamata a scrollIntoView() ha un'interazione divertente con CSS che merita di essere menzionata.

.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 scorrimento orizzontale, abbiamo incorporato una query supporti che applica lo scorrimento smooth se l'utente è sensibile al movimento. JavaScript può effettuare liberamente chiamate per visualizzare gli elementi di scorrimento e CSS può gestire l'esperienza utente in modo dichiarativo. A volte formano una coppia deliziosa.

Conclusione

Ora che sai come ho fatto, come faresti tu? In questo modo si ottiene un'architettura dei componenti divertente. Chi realizzerà la prima versione con gli slot nel suo framework preferito? 🙂

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

Remix della community