<button class="rtds-tab is-tab rtds-tab--default is-disabled" disabled>
<span class="rtds-tab__label">Tab disabilitato</span>
</button>
<{% if href %}a href="{{ href }}"{% else %}button{% endif %} class="rtds-tab is-tab{% block classes %} rtds-tab--default {{ classes }}{% endblock %}{% if isActive %} is-active{% endif %}{% if isDisabled %} is-disabled{% endif %}"
{% if roleTab %}role="tab"{% endif %}
{% if id %}id="{{ id }}"{% endif %}
{% if ariaSelected %}aria-selected="{{ ariaSelected }}"{% endif %}
{% if tabIndex or (isDisabled and href) %}tabindex="-1"{% endif %}
{% if ariaControls %}aria-controls="{{ ariaControls }}"{% endif %}
{% if ariaCurrent %}aria-current="{{ ariaCurrent }}"{% endif %}
{% if isDisabled %}{% if not href %}disabled{% else %}aria-disabled="true"{% endif %}{% endif %}
>
{% if iconLeft %}
{% render '@icon--small', { id: iconLeft, size: 'rtds-w-4 rtds-h-4 md:rtds-w-5 md:rtds-h-5 lg:rtds-w-6 lg:rtds-h-6' }, true %}
{% endif %}
<span class="rtds-tab__label{% if iconOnly %} rtds-sr-only{% endif %}">{{ label }}</span>
{% if iconRight %}
{% render '@icon--small', { id: iconRight, size: 'rtds-w-4 rtds-h-4 md:rtds-w-5 md:rtds-h-5 lg:rtds-w-6 lg:rtds-h-6' }, true %}
{% endif %}
</{% if href %}a{% else %}button{% endif %}>
{
"label": "Tab disabilitato",
"href": "",
"isDisabled": true
}
/**
* TAB
*
*
*/
/* BASE */
.rtds-tab {
display: flex;
justify-content: center;
font-weight: 700;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.rtds-tab:hover {
background-color: var(--color-background-02);
}
.rtds-tab--default {
border-bottom-width: 2px;
border-color: transparent;
padding: var(--spacing-2);
font-size: var(--fontsize-text-sm);
color: var(--color-content-02);
}
.rtds-tab--default:hover {
background-color: var(--color-background-02);
}
@media (min-width: 768px) {
.rtds-tab--default {
padding: var(--spacing-3);
font-size: var(--fontsize-text-base);
}
}
.rtds-tab--default.is-active,
.rtds-tab--default[aria-selected="true"] {
border-color: currentColor;
color: var(--color-content-primary);
}
/* BORDERLESS */
.rtds-tab--borderless {
padding: var(--spacing-4);
background-color: var(--color-background-03);
}
@media (min-width: 1024px) {
.rtds-tab--borderless {
padding-top: var(--spacing-6);
padding-bottom: var(--spacing-6);
padding-left: var(--spacing-12);
padding-right: var(--spacing-12);
}
}
@media (min-width: 1280px) {
.rtds-tab--borderless {
padding-top: var(--spacing-6);
padding-bottom: var(--spacing-6);
padding-left: var(--spacing-16);
padding-right: var(--spacing-16);
}
}
.rtds-tab--borderless.is-active,
.rtds-tab--borderless[aria-selected="true"] {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
color: var(--color-content-primary);
}
.rtds-tab--icon-label {
gap: var(--spacing-2);
}
/* DISABLED */
.rtds-tab.is-disabled,
.rtds-tab.is-disabled:hover {
color: var(--color-content-disabled);
background-color: transparent;
border-color: transparent;
cursor: not-allowed;
}
/**
* TAB
*
*
*/
/* BASE */
@layer components {
.rtds-tab {
@apply rtds-flex rtds-justify-center rtds-font-bold hover:rtds-background-02 rtds-transition-all;
}
.rtds-tab--default {
@apply rtds-content-02 rtds-p-2 md:rtds-p-3 rtds-border-b-2 rtds-border-transparent rtds-text-sm md:rtds-text-base hover:rtds-background-02;
}
.rtds-tab--default.is-active,
.rtds-tab--default[aria-selected="true"] {
@apply rtds-content-primary rtds-border-current;
}
/* BORDERLESS */
.rtds-tab--borderless {
@apply rtds-p-4 lg:rtds-py-6 lg:rtds-px-12 xl:rtds-py-6 xl:rtds-px-16 rtds-background-03;
}
.rtds-tab--borderless.is-active,
.rtds-tab--borderless[aria-selected="true"] {
@apply rtds-bg-white rtds-content-primary;
}
.rtds-tab--icon-label {
@apply rtds-gap-2;
}
/* DISABLED */
.rtds-tab.is-disabled,
.rtds-tab.is-disabled:hover {
color: var(--color-content-disabled);
background-color: transparent;
border-color: transparent;
cursor: not-allowed;
}
}
Il componente Tab gestisce la visualizzazione di tab singoli, utilizzabile sia come link che come pulsante. È progettato per essere accessibile e responsive, supportando diverse configurazioni di layout e stile.
Per il pattern completo con gruppo di tab, pannelli di contenuto o navigazione a sezioni, vedi il componente
@tablist(04-organisms/tablist), che usa questo componente come building block.
Il componente può essere renderizzato come:
<a href="..."> — quando viene specificato href nel contesto Fractal/Nunjucks<button> — quando href non è specificato nel contesto Fractal/NunjucksIl tag HTML non determina da solo il comportamento semantico: il contesto d’uso definisce quali attributi ARIA applicare. Esistono tre scenari distinti.
<button>)Il <button> è usato per attivare/disattivare un pannello tramite JavaScript. In questo caso il tab fa parte di una struttura role="tablist" e richiede la semantica ARIA completa del pattern tab:
role="tab" sull’elementoaria-selected="true|false" per comunicare lo statoaria-controls="[id-del-tabpanel]" per collegare il tab al pannellotabindex="-1" sui tab non selezionati (navigazione con frecce, non con Tab)<a>)Un <a> può essere usato come tab all’interno di una struttura tablist gestita via JavaScript, esattamente come il <button>. In questo caso si applicano gli stessi attributi ARIA dello Scenario A. L’href dovrebbe puntare all’id del tabpanel (es. href="#panel-1"), così il collegamento semantico è preservato anche senza JS.
Questo scenario richiede che il componente tablist parent sovrascriva il comportamento nativo del link (prevenendo la navigazione e gestendo l’attivazione via JS).
Un <a> può essere usato come elemento di navigazione tradizionale — link diretto a un’altra pagina o link ad ancora a una sezione della stessa pagina. In questo caso il tab non fa parte di un tablist e non deve avere role="tab", aria-selected, aria-controls o tabindex="-1". L’elemento mantiene il suo ruolo implicito di link.
Se il link corrisponde alla pagina o sezione corrente, si può usare aria-current="page" o aria-current="true" al posto di aria-selected.
default: Variante base del tabborderless: Variante senza bordoactive: Variante con stato attivo (isActive: true) — usare nel pattern navigazione (Scenario C). Nel pattern tablist, lo stato attivo deriva da aria-selected="true" via CSS, non da questa classe.disabled: Variante con stato disabilitato su <a> — aria-disabled="true", tabindex="-1", classe is-disabled (isDisabled: true)disabled-button: Variante con stato disabilitato su <button> — attributo disabled, classe is-disabled (isDisabled: true, href: '')Le seguenti varianti mostrano il markup corretto per i due scenari principali di integrazione:
tablist-attivo: Pattern tablist ARIA — <button> selezionato con role="tab", aria-selected="true", aria-controlstablist-inattivo: Pattern tablist ARIA — <button> non selezionato con tabindex="-1", aria-selected="false"navigazione-attivo: Pattern navigazione — <a> con aria-current="page" per il link attivonavigazione-inattivo: Pattern navigazione — <a> senza attributi ARIA aggiuntiviLe varianti visive presentate in Fractal mostrano la resa grafica del componente ma non riflettono necessariamente i diversi pattern di utilizzo documentati sopra. Fare riferimento agli Esempi di utilizzo per i markup corretti nei tre scenari.
Contesto di sviluppo Fractal/Nunjucks — Questa sezione riguarda esclusivamente i parametri del context
.config.ymlusati durante lo sviluppo nella libreria. Non descrive attributi HTML o proprietà da usare nei temi, CMS o ambienti di implementazione: per quello, fare riferimento alla sezione Esempi di utilizzo.
label (stringa, obbligatorio): Etichetta del tabhref (stringa, opzionale): URL o hash — se specificato, il componente è un <a>, altrimenti un <button>id (stringa, opzionale): ID del tabroleTab (booleano, opzionale): Se true, aggiunge role="tab" (uso in tablist — Scenari A e B)ariaSelected (booleano, opzionale): Valore per aria-selected (uso in tablist — Scenari A e B)ariaControls (stringa, opzionale): ID del pannello tab correlato (uso in tablist — Scenari A e B)ariaCurrent (stringa, opzionale): Valore per aria-current (page, step, location, date, time, true) — uso come link di navigazione (Scenario C)tabIndex (booleano, opzionale): Se true, aggiunge tabindex="-1" (tab non selezionato in tablist — Scenari A e B)iconLeft (booleano, opzionale): Se true, mostra un’icona a sinistraiconRight (booleano, opzionale): Se true, mostra un’icona a destraborderColor (stringa, opzionale): Colore del bordo per lo stato attivo (default: rtds-border-gray-04)bgColor (stringa, opzionale): Colore di sfondobgActiveColor (stringa, opzionale): Colore di sfondo per lo stato attivoisActive (booleano, opzionale): Se true, aggiunge la classe is-active — usare solo per il pattern navigazione (Scenario C). Nel pattern tablist (Scenari A e B) lo stato attivo è gestito da JavaScript via aria-selected; la classe is-active non deve essere usata.isDisabled (booleano, opzionale): Se true, aggiunge la classe is-disabled; su <button> aggiunge disabled; su <a> aggiunge aria-disabled="true" e tabindex="-1"classes (stringa, opzionale): Classi CSS aggiuntiveLivello AA — criteri pertinenti:
role="tab" e aria-selected comunicano la struttura e lo stato semantico; quando usato come link, il ruolo implicito link è sufficiente<a> (link) o <button>, entrambi focalizzabili da tastieraaria-selected riflette lo stato di selezione nel pattern tablist; aria-controls collega il tab al pannello; aria-current indica la posizione corrente quando usato come link di navigazione<a> — ruolo implicito link; attivabile con Enterrole="tab" sovrascrive il ruolo implicito; aria-selected comunica lo stato; href punta all’id del tabpanellink; non aggiungere attributi del pattern tablist<button> — ruolo implicito button; attivabile con Enter e Space; usare sempre in tablist (Scenario A) con role="tab" e aria-selected<span class="rtds-tab__label">; nella versione icona-only, la label ha classe rtds-sr-only — visivamente nascosta ma disponibile agli screen reader<button> usare disabled (il browser lo esclude automaticamente dalla navigazione Tab); su <a> usare aria-disabled="true" + tabindex="-1" (il link non è nativo-disabilitabile, va escluso manualmente dal focus). Usare sempre isDisabled: true per ottenere entrambi l’attributo e la classe is-disabled.| Attributo | Scenari A e B (tablist) | Scenario C (link navigazione) |
|---|---|---|
role="tab" |
Obbligatorio | Non usare |
aria-selected |
Obbligatorio | Non usare |
aria-controls |
Raccomandato | Non usare |
tabindex="-1" |
Sui tab non selezionati | Non usare |
aria-current |
Non usare | Opzionale ("page", ecc.) |
| Tasto | Azione |
|---|---|
Tab / Shift+Tab |
Sposta il focus verso/dal tab (Scenario C — link); raggiunge solo il tab selezionato in tablist (Scenari A e B) |
Enter |
Attiva il tab |
Space |
Attiva il tab (solo <button>) |
| Frecce direzionali | Navigazione tra i tab in tablist (gestita dal componente tablist parent, non da questo componente) |
Tab disabilitati (
.is-disabled): i<button disabled>sono esclusi automaticamente dal browser; i<a aria-disabled="true" tabindex="-1">sono esclusi viatabindex. In entrambi i casi il tab non è raggiungibile con la tastiera.
:focus-visible è sempre visibile e non viene rimosso via CSStabindex="-1" per escluderli dall’ordine di focus Tab; la navigazione avviene con le frecce direzionaliaria-selected (selezionato/deselezionato)aria-current="page" è presente, viene annunciata anche la posizione corrente<span class="rtds-tab__label rtds-sr-only"> contiene la label — visivamente nascosta ma disponibile agli screen reader<button> in tablistLo stato attivo è comunicato da aria-selected="true" — gestito via JavaScript. Non aggiungere is-active ai button in questo pattern: il CSS applica lo stile attivo tramite [aria-selected="true"] e il JS non rimuove mai la classe, causando due tab visivamente attivi contemporaneamente.
<!-- Tab selezionato — aria-selected="true" dà lo stile attivo -->
<button class="rtds-tab rtds-tab--default is-tab" role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">
<span class="rtds-tab__label">Prima scheda</span>
</button>
<!-- Tab non selezionato -->
<button class="rtds-tab rtds-tab--default is-tab" role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
<span class="rtds-tab__label">Seconda scheda</span>
</button><a> in tablist customCome nello Scenario A, lo stato attivo è gestito da JavaScript via aria-selected. Non aggiungere is-active.
<!-- Tab selezionato — href punta all'id del tabpanel -->
<a href="#panel-1" class="rtds-tab rtds-tab--default is-tab" role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">
<span class="rtds-tab__label">Prima scheda</span>
</a>
<!-- Tab non selezionato -->
<a href="#panel-2" class="rtds-tab rtds-tab--default is-tab" role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
<span class="rtds-tab__label">Seconda scheda</span>
</a><a> come link di navigazioneLo stato attivo è determinato server-side (nessun JS). La classe is-active è il meccanismo corretto in questo contesto.
<!-- Link attivo nella navigazione corrente -->
<a href="/sezione-corrente/" class="rtds-tab rtds-tab--default is-tab is-active" aria-current="page">
<span class="rtds-tab__label">Sezione corrente</span>
</a>
<!-- Link ad ancora nella stessa pagina -->
<a href="#sezione-2" class="rtds-tab rtds-tab--default is-tab">
<span class="rtds-tab__label">Sezione 2</span>
</a>Il CSS definisce due selettori equivalenti per lo stile attivo:
.rtds-tab--default.is-active,
.rtds-tab--default[aria-selected="true"] { … }I due meccanismi sono intenzionalmente distinti e non intercambiabili:
| Meccanismo | Contesto | Gestito da |
|---|---|---|
[aria-selected="true"] |
Scenari A e B — tablist ARIA con JS | JavaScript (tablist.js) |
.is-active |
Scenario C — navigazione link | Server-side / statico (NJK) |
Nel pattern tablist non aggiungere is-active ai tab: tablist.js gestisce solo aria-selected e non rimuove mai la classe. Se is-active è presente sul primo tab, al cambio scheda quel tab mantiene lo stile attivo anche dopo che JS imposta aria-selected="false".
aria-selected sono responsabilità del componente tablist parentaria-selected e tabindex via JavaScript@tablist (04-organisms/tablist)