# Field

Grupo semântico de label, controle, texto de apoio e erro para campos de formulário.

> 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: Forms
- Status: stable
- Relacionados: /componentes/textfield.md, /componentes/select.md, /componentes/checkbox.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 (`field`, `input`) 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 {
  Field,
  FieldDescription,
  FieldGroup,
  FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";

export function ProfileField() {
  return (
    <FieldGroup className="max-w-sm">
      <Field>
        <FieldLabel htmlFor="nome">Nome</FieldLabel>
        <Input id="nome" name="nome" type="text" aria-describedby="nome-help" />
        <FieldDescription id="nome-help">Como aparece no documento.</FieldDescription>
      </Field>
    </FieldGroup>
  );
}
```

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

```vue
<script setup lang="ts">
import {
  Field,
  FieldDescription,
  FieldGroup,
  FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
</script>

<template>
  <FieldGroup class="max-w-sm">
    <Field>
      <FieldLabel for="nome">Nome</FieldLabel>
      <Input id="nome" name="nome" type="text" aria-describedby="nome-help" />
      <FieldDescription id="nome-help">Como aparece no documento.</FieldDescription>
    </Field>
  </FieldGroup>
</template>
```

**HTML (copy-first)**

```html
<div class="itps-field">
  <label for="nome" class="itps-field__label">Nome</label>
  <input id="nome" name="nome" type="text" class="itps-input" aria-describedby="nome-help" />
  <p id="nome-help" class="itps-field__hint">Como aparece no documento.</p>
</div>
```

## Instalação

O comando instala o Field e o Input 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/field
```

**terminal · Vue 3/Nuxt 4**

```sh
pnpm dlx shadcn-vue@latest add https://dsystem.sinapse.org.br/r/vue/field.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 `itps-field` para agrupar label, controle e mensagens do campo. Conecte textos de apoio ou erro pelo `aria-describedby` do controle.

Em React/Next com shadcn/ui e Vue 3/Nuxt 4 com shadcn-vue, use `Field`, `FieldLabel`, `FieldDescription`, `FieldError` e o controle da stack. Para HTML ou wrappers finos, mantenha `label`, controle e mensagens dentro de `itps-field`.

## Exemplos

### Obrigatório e validação

Use `required` no controle para obrigatoriedade. Em erro, marque o controle com `aria-invalid="true"` e aponte para a mensagem.

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

```tsx
import {
  Field,
  FieldError,
  FieldGroup,
  FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";

export function EmailField() {
  return (
    <FieldGroup className="max-w-sm">
      <Field>
        <FieldLabel htmlFor="email">
          E-mail <span aria-hidden="true">*</span>
        </FieldLabel>
        <Input
          id="email"
          name="email"
          type="email"
          defaultValue="abc"
          required
          aria-invalid="true"
          aria-describedby="email-error"
        />
        <FieldError id="email-error">Formato inválido.</FieldError>
      </Field>
    </FieldGroup>
  );
}
```

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

```vue
<script setup lang="ts">
import {
  Field,
  FieldError,
  FieldGroup,
  FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
</script>

<template>
  <FieldGroup class="max-w-sm">
    <Field>
      <FieldLabel for="email">
        E-mail <span aria-hidden="true">*</span>
      </FieldLabel>
      <Input
        id="email"
        name="email"
        type="email"
        value="abc"
        required
        aria-invalid="true"
        aria-describedby="email-error"
      />
      <FieldError id="email-error">Formato inválido.</FieldError>
    </Field>
  </FieldGroup>
</template>
```

**HTML (copy-first)**

```html
<div class="itps-field">
  <label for="email" class="itps-field__label">
    E-mail
    <span class="itps-field__required" aria-hidden="true">*</span>
    <span class="itps-field__tooltip" title="Use o e-mail institucional." aria-label="Use o e-mail institucional.">?</span>
  </label>
  <input
    id="email"
    name="email"
    type="email"
    value="abc"
    class="itps-input itps-input--error"
    required
    aria-invalid="true"
    aria-describedby="email-error"
  />
  <p id="email-error" class="itps-field__error">
    <i class="ti ti-alert-circle" aria-hidden="true"></i>
    <span>Formato inválido.</span>
  </p>
</div>
```

## API copy-first

| Classe/atributo | Uso |
|-----------------|-----|
| `itps-field` | Agrupa label, controle e mensagens em uma coluna. |
| `itps-field__label` | Label principal do controle. |
| `itps-field__required` | Indicador visual de obrigatoriedade; use com `aria-hidden="true"`. |
| `itps-field__hint` | Texto de apoio conectado por `aria-describedby`. |
| `itps-field__error` | Mensagem de erro conectada por `aria-describedby`. |
| `itps-field__tooltip` | Indicador curto de informação complementar ao label. |
| `for` / `id` | Liga o label ao controle. |
| `aria-describedby` | Liga o controle ao hint ou erro. |
| `aria-invalid="true"` | Comunica estado inválido. |
| `required` | Comunica obrigatoriedade ao navegador e tecnologias assistivas. |

## Acessibilidade

O label deve apontar para o controle com `for`/`id`. Texto de apoio e erro devem ser referenciados por `aria-describedby`. Em erro, adicione `aria-invalid="true"` ao controle. Use `required` no controle; o asterisco é apenas apoio visual.
