Separadores

Dá a cada item um href e os separadores passam a ligações — uma página por separador, sem script, com aria-current no ativo. Dá a cada item um panel e passam a ser um grupo de rádios cujos painéis se trocam no lugar, e continua sem script.

Num site estático a forma em ligações costuma ser a certa: dá um URL a cada vista e sobrevive a ter o JavaScript desligado. Usa painéis quando o conteúdo for pequeno e trocar não deva custar uma navegação.

Separadores em ligações

São mesmo ligações: clicar navega. O sublinhado vem de active ou de value no build, não do clique, por isso cada página marca o seu próprio separador. Um separador em ligação não reage sozinho ao URL: para isso, troca no lugar com os painéis mais abaixo.

tabs({
  items: [
    { label: 'Migalhas', href: '/pt/ui/breadcrumbs' },
    { label: 'Separadores', href: '/pt/ui/tabs', active: true },
    { label: 'Paginação', href: '/pt/ui/pagination' },
  ],
})

Separadores com painéis

O separador é uma <label> de um rádio que a folha de estilos mantém fora de vista, e o painel que se segue ao rádio marcado é o que o CSS mostra. Esta página não importa nada: trocar, e andar entre os separadores com as setas, é o que um grupo de rádios já faz.

npm install sitelo javascript-to-html
import * as ui from 'sitelo/ui'
sitelo build
tabs({
  value: 'install',
  items: [
    { id: 'install', label: 'Instalar', panel: card({ variant: 'flat' }, cardBody(code('npm install sitelo javascript-to-html'))) },
    { id: 'use', label: 'Usar', panel: card({ variant: 'flat' }, cardBody(code("import * as ui from 'sitelo/ui'"))) },
    { id: 'build', label: 'Construir', panel: card({ variant: 'flat' }, cardBody(code('sitelo build'))) },
  ],
})

Separadores com ligação

Dá também aos itens com painel um href com fragmento e os rádios dão lugar a ligações: o URL nomeia o separador, o :target aponta-o, aparece o painel que vem a seguir, e a escolha sobrevive a um recarregamento, a uma ligação partilhada e ao botão de retroceder. O id fica no separador e não no painel porque o browser leva ao topo da janela aquilo que o URL nomeia — no painel, empurraria os separadores para fora do ecrã onde acabaste de clicar. Só um elemento por documento pode ser :target, por isso esta forma é para um único conjunto de separadores por página. O deslize em si não se cancela: seguir um fragmento é mover a janela. Só se escolhe para onde vai e onde pousa — é para isso que servem o id no separador e o seu scroll-margin-block-start, que se define com a prop scrollMargin: dá a um cabeçalho fixo pelo menos a altura dele.

Preparar

Este painel é o #tab-setup: copia o URL e ele volta.

Publicar

E este é o #tab-deploy.

tabs({
  items: [
    { id: 'setup', label: 'Preparar', href: '#tab-setup', panel: card({ variant: 'flat' }, cardBody(text({ variant: 'small' }, 'Este painel é o #tab-setup: copia o URL e ele volta.'))) },
    { id: 'deploy', label: 'Publicar', href: '#tab-deploy', panel: card({ variant: 'flat' }, cardBody(text({ variant: 'small' }, 'E este é o #tab-deploy.'))) },
  ],
})

Pastilhas

src/index.ht.js
src/index.ht.ts
src/index.ht.jsx
stack({ gap: 'lg' },
  tabs({
    variant: 'pills',
    items: [
      { label: 'Tudo', href: '#all', active: true },
      { label: 'Guias', href: '#guides' },
      { label: 'Exemplos', href: '#examples' },
    ],
  }),
  tabs({
    variant: 'pills',
    value: 'js',
    items: [
      { id: 'js', label: 'JavaScript', panel: card({ variant: 'flat' }, cardBody(code('src/index.ht.js'))) },
      { id: 'ts', label: 'TypeScript', panel: card({ variant: 'flat' }, cardBody(code('src/index.ht.ts'))) },
      { id: 'jsx', label: 'JSX', panel: card({ variant: 'flat' }, cardBody(code('src/index.ht.jsx'))) },
    ],
  }),
)

Cores

stack({ gap: 'lg' },
  tabs({ color: 'primary', items: [{ label: 'Primary', href: '#p', active: true }, { label: 'Outro', href: '#p2' }] }),
  tabs({ color: 'neutral', items: [{ label: 'Neutral', href: '#n', active: true }, { label: 'Outro', href: '#n2' }] }),
  tabs({ color: 'danger', items: [{ label: 'Danger', href: '#d', active: true }, { label: 'Outro', href: '#d2' }] }),
)

Muitos separadores

A lista desliza na horizontal em vez de mudar de linha, por isso a fila mantém a forma num telemóvel. Os separadores com painel mudam de linha: cada painel tem de vir a seguir ao seu separador, por isso não sobra fila nenhuma para deslizar.

tabs({
  items: [
    'Visão geral', 'Rotas', 'Dados', 'Recursos', 'Imagens', 'Ilhas', 'TypeScript', 'CLI', 'Publicação',
  ].map((label, index) => ({ label, href: '#many-' + index, active: index === 0 })),
})

Desativado

Este funciona.

tabs({
  value: 'now',
  items: [
    { id: 'now', label: 'Disponível', panel: card({ variant: 'flat' }, cardBody(text({ variant: 'small' }, 'Este funciona.'))) },
    { id: 'soon', label: 'Em breve', disabled: true, panel: card({ variant: 'flat' }, cardBody('')) },
  ],
})

Acessibilidade

A forma com painéis é um grupo de rádios a sério: os separadores são elementos <label> de rádios que partilham um name, por isso um leitor de ecrã anuncia qual de quantos está escolhido, e as setas, o Home e o End funcionam sem carregar nada. A forma com ligação são ligações simples e não leva aria-current — seria escrito uma vez e ficaria errado ao primeiro clique. De propósito não é uma tablist ARIA — aria-selected é escrito uma vez, no servidor, e o CSS não o consegue manter verdadeiro à medida que clicas. A forma em ligações também não é uma tablist: ligações que navegam são ligações, e dar-lhes semântica de separador seria mentir sobre o que fazem.

Props

PropTipoPredefiniçãoDescrição
itemsArray[]Cadeias, ou objetos { id, label, href, panel, active, disabled }.
valuestring—Id do item ativo. Recai sobre active, e depois sobre o primeiro.
variant'underline' | 'pills''underline'Como o separador ativo é marcado.
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''primary'Cor do separador ativo.
labelstring'Tabs'Nome acessível do grupo. Só na forma com painéis.
namestringid do primeiro itemNome do grupo de rádios. Só é preciso com dois conjuntos de separadores com painel na mesma página.
hrefstring—Num item: uma página para ligar ou, com panel, o fragmento que lhe dá nome.
scrollMarginSpace'md'Quanto espaço a janela deixa por cima do separador. Só na forma :target.