# Pagination

Controles de navegação entre páginas com contrato copy-first e composição shadcn.

> Documentação do Sinapse DS. Snippets shadcn usam os componentes do seu projeto (`@/components/ui/*`); snippets HTML usam as classes `itps-*` do CSS Sinapse (`/css/<versão>/`, veja `/comecar.md`). Componentes do site de documentação não fazem parte do DS.

- Categoria: Ações & Navegação
- Status: stable
- Relacionados: /componentes/button.md, /componentes/breadcrumb.md

## Em cada stack

- HTML e Astro: Classes `itps-*` do CSS Sinapse, com todas as variantes desta página.
- React e Vue: Componentes padrão do shadcn (`pagination`) com o tema Sinapse: cores, fonte e raio vêm do tema, e as variantes `itps-*` desta página existem só no HTML.

**React/Next (shadcn/ui)**

```tsx
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/ui/pagination'

export function PaginationExample() {
  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious href="?page=1" aria-disabled="true" className="pointer-events-none opacity-50" />
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="?page=1">1</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="?page=2" isActive>2</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationLink href="?page=3">3</PaginationLink>
        </PaginationItem>
        <PaginationItem>
          <PaginationEllipsis />
        </PaginationItem>
        <PaginationItem>
          <PaginationNext href="?page=3" />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  )
}
```

**Vue 3/Nuxt 4 (shadcn-vue)**

```vue
<script setup lang="ts">
import {
  Pagination,
  PaginationContent,
  PaginationEllipsis,
  PaginationItem,
  PaginationLink,
  PaginationNext,
  PaginationPrevious,
} from '@/components/ui/pagination'
</script>

<template>
  <Pagination>
    <PaginationContent>
      <PaginationItem>
        <PaginationPrevious href="?page=1" aria-disabled="true" class="pointer-events-none opacity-50" />
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="?page=1">1</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="?page=2" is-active>2</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationLink href="?page=3">3</PaginationLink>
      </PaginationItem>
      <PaginationItem>
        <PaginationEllipsis />
      </PaginationItem>
      <PaginationItem>
        <PaginationNext href="?page=3" />
      </PaginationItem>
    </PaginationContent>
  </Pagination>
</template>
```

**HTML (copy-first)**

```html
<nav class="itps-pagination" aria-label="Paginação">
  <button type="button" class="itps-pagination__button" aria-label="Página anterior" disabled>
    <i class="ti ti-chevron-left" aria-hidden="true"></i>
  </button>

  <input type="number" value="1" min="1" max="10" class="itps-pagination__input" aria-label="Número da página atual" />

  <span class="itps-pagination__label">de 10</span>

  <button type="button" class="itps-pagination__button" aria-label="Próxima página">
    <i class="ti ti-chevron-right" aria-hidden="true"></i>
  </button>
</nav>
```

## Instalação

O comando instala o Pagination da sua stack com o tema Sinapse. Configure o registry uma vez por projeto em [Comece por aqui](/comecar).

**terminal · React/Next**

```sh
pnpm dlx shadcn@latest add @sinapse/pagination
```

**terminal · Vue 3/Nuxt 4**

```sh
pnpm dlx shadcn-vue@latest add https://dsystem.sinapse.org.br/r/vue/pagination.json
```

Sem shadcn, não há instalação por componente: use o CSS Sinapse e as classes da API copy-first desta página.

## Uso

Use Pagination quando o usuário precisa navegar por listas, tabelas ou resultados longos mantendo página atual, total e extremos claros.

Em React/Next com shadcn/ui e Vue 3/Nuxt 4 com shadcn-vue, use os componentes compostos de `@/components/ui/pagination`. Para HTML ou wrappers finos, use `itps-pagination` diretamente e conecte os handlers de navegação na sua stack.

## Exemplos

### Estrutura

| Peça | React/Next e Vue 3/Nuxt 4 | HTML |
|------|--------|------------|
| Landmark | `Pagination` | `<nav class="itps-pagination">` |
| Lista e item | `PaginationContent`, `PaginationItem` | filhos diretos do nav |
| Página numérica | `PaginationLink` | `itps-pagination__input` ou botão local |
| Anterior/próxima | `PaginationPrevious`, `PaginationNext` | `itps-pagination__button` |
| Atalho de extremo | composição do projeto | `itps-pagination__button--boundary` |
| Total | composição textual | `itps-pagination__label` |

### Extremos e estado desabilitado

Use atalhos de primeira/última página em tabelas longas. Nos extremos, desabilite o controle que não pode executar ação. Nos links do shadcn, prefira `aria-disabled` com estado visual; em botão copy-first, use `disabled`.

**HTML (copy-first)**

```html
<div class="flex flex-col items-start gap-4">
  <nav class="itps-pagination" aria-label="Paginação na primeira página">
    <button type="button" class="itps-pagination__button itps-pagination__button--boundary" aria-label="Primeira página" disabled>
      <i class="ti ti-chevron-left-pipe" aria-hidden="true"></i>
    </button>

    <button type="button" class="itps-pagination__button" aria-label="Página anterior" disabled>
      <i class="ti ti-chevron-left" aria-hidden="true"></i>
    </button>

    <input type="number" value="1" min="1" max="5" class="itps-pagination__input" aria-label="Número da página atual" />
    <span class="itps-pagination__label">de 5</span>

    <button type="button" class="itps-pagination__button" aria-label="Próxima página">
      <i class="ti ti-chevron-right" aria-hidden="true"></i>
    </button>

    <button type="button" class="itps-pagination__button itps-pagination__button--boundary" aria-label="Última página">
      <i class="ti ti-chevron-right-pipe" aria-hidden="true"></i>
    </button>
  </nav>

  <nav class="itps-pagination" aria-label="Paginação na última página">
    <button type="button" class="itps-pagination__button itps-pagination__button--boundary" aria-label="Primeira página">
      <i class="ti ti-chevron-left-pipe" aria-hidden="true"></i>
    </button>

    <button type="button" class="itps-pagination__button" aria-label="Página anterior">
      <i class="ti ti-chevron-left" aria-hidden="true"></i>
    </button>

    <input type="number" value="5" min="1" max="5" class="itps-pagination__input" aria-label="Número da página atual" />
    <span class="itps-pagination__label">de 5</span>

    <button type="button" class="itps-pagination__button" aria-label="Próxima página" disabled>
      <i class="ti ti-chevron-right" aria-hidden="true"></i>
    </button>

    <button type="button" class="itps-pagination__button itps-pagination__button--boundary" aria-label="Última página" disabled>
      <i class="ti ti-chevron-right-pipe" aria-hidden="true"></i>
    </button>
  </nav>
</div>
```

## API copy-first

| Classe/atributo | Uso |
|-----------------|-----|
| `itps-pagination` | Contêiner em linha com gap estável para controles de página. |
| `itps-pagination__button` | Botão circular para anterior/próxima ou ações de navegação. |
| `itps-pagination__button--boundary` | Botão de primeira/última página com borda e fundo secundário. |
| `itps-pagination__input` | Campo numérico da página atual, limitado por `min` e `max`. |
| `itps-pagination__label` | Texto auxiliar para total de páginas. |
| `aria-label` | Nomeia o nav e os botões apenas com ícone. |
| `disabled` | Bloqueia botões copy-first que não podem navegar. |
| `aria-disabled="true"` | Comunica estado indisponível em links shadcn. |
| `min` / `max` | Limitam o campo numérico ao intervalo real de páginas. |
| `aria-hidden="true"` | Esconde ícones decorativos de tecnologias assistivas. |

## Acessibilidade

Use um `<nav>` com `aria-label` específico para diferenciar paginações quando houver mais de uma na tela. Botões apenas com ícone precisam de `aria-label`; os ícones decorativos ficam com `aria-hidden="true"`. Campos numéricos devem ter `min`, `max` e nome acessível. Em links desabilitados, use `aria-disabled="true"` e remova handlers de ativação.
