Form Field
Wrapper composite que junta label, controle (Input/Select/Textarea), required indicator, helper text e error message. É onde a acessibilidade de form se materializa: associação correta de label↔controle, aria-describedby pra helper, aria-invalid em estado de erro.Composite wrapper that pairs label, control (Input/Select/Textarea), required indicator, helper text, and error message. This is where form accessibility lives: correct label↔control association, aria-describedby for helpers, aria-invalid on error.
Quando usarWhen to use
aria-label direto no controle). Mesmo assim, considere usar Form Field com --no-label para manter consistência de espaçamento.The control doesn't need a visible label (e.g., search input in header with icon, using aria-label directly). Even so, consider Form Field with --no-label to keep spacing consistent.
AnatomiaAnatomy
.ds-field__label) — texto descritivo, com for= apontando pro id do controle.2 Required indicator (
.ds-field__required) — asterisco visual, aria-hidden="true" (a obrigatoriedade vem do aria-required no controle).3 Controle — Input/Select/Textarea/Checkbox group, com
id matching o label e aria-describedby apontando pro helper.4 Helper text (
.ds-field__helper) — orientação ou contexto adicional, com id referenciado pelo aria-describedby do controle.5 Error message (
.ds-field__error) — ícone + mensagem de validação, aparece apenas em .ds-field--error com role="alert".
1 Label (.ds-field__label) — descriptive text with for= pointing to the control's id.2 Required indicator (
.ds-field__required) — visual asterisk, aria-hidden="true" (required state comes from aria-required on the control).3 Control — Input/Select/Textarea/Checkbox group, with matching
id and aria-describedby pointing to the helper.4 Helper text (
.ds-field__helper) — guidance or additional context, with id referenced by control's aria-describedby.5 Error message (
.ds-field__error) — icon + validation message, shown only in .ds-field--error with role="alert".
Encapsulando controlesWrapping form controls
Form Field é agnóstico ao tipo de controle. Funciona com Input, Textarea e Select via label[for]. Para grupos (Checkbox/Radio), use <fieldset> + <legend> em vez de label/for individual — a semântica de grupo é diferente da semântica de controle único.Form Field is control-agnostic. Works with Input, Textarea, and Select via label[for]. For groups (Checkbox/Radio), use <fieldset> + <legend> instead of individual label/for — group semantics differ from single-control semantics.
Nota: para grupos, aria-describedby vai no container do grupo; <legend> substitui <label>. O wrapper <fieldset class="ds-field"> herda o gap vertical igual ao Input.Note: for groups, aria-describedby goes on the group container; <legend> replaces <label>. The <fieldset class="ds-field"> wrapper inherits the same vertical gap as Input.
Error stateError state
Adicione .ds-field--error no wrapper, aria-invalid="true" + aria-describedby no controle apontando para o id do error. role="alert" no error garante anúncio imediato para screen readers.Add .ds-field--error on the wrapper, aria-invalid="true" + aria-describedby on the control pointing to the error's id. role="alert" on the error ensures immediate screen reader announcement.
<div class="ds-field ds-field--error">
<div class="ds-field__label-row">
<label class="ds-field__label" for="email">Email address</label>
<span class="ds-field__required" aria-hidden="true">*</span>
</div>
<div class="ds-input ds-input--error">
<input type="email" id="email" class="ds-input__field"
value="not-an-email"
aria-required="true"
aria-invalid="true"
aria-describedby="email-error">
</div>
<span class="ds-field__error" id="email-error" role="alert">
Please enter a valid email address.
</span>
</div>
Boas práticasBest practices
label[for], id e aria-describedby para conectar visual e semântica.Use label[for], id, and aria-describedby to connect visuals and semantics.Checklist de acessibilidadeAccessibility checklist
labelcomfor=matching oiddo controle (WCAG 1.3.1, 3.3.2).labelwithfor=matching the control'sid(WCAG 1.3.1, 3.3.2).- Required:
aria-required="true"no controle. Asterisco visual comaria-hidden="true".Required:aria-required="true"on the control. Visual asterisk witharia-hidden="true". - Helper text:
aria-describedbyno controle apontando proiddo helper.Helper text:aria-describedbyon the control pointing to helper'sid. - Error:
aria-invalid="true"no controle,aria-describedbyapontando proiddo error, erole="alert"no error pra anúncio imediato.Error:aria-invalid="true"on the control,aria-describedbypointing to error'sid, androle="alert"on the error for immediate announcement. - Sem label visível: use
aria-labeldireto no controle E aplique.ds-field--no-labelpra esconder a row.No visible label: usearia-labelon the control directly AND apply.ds-field--no-labelto hide the row. - Grupos (Checkbox/Radio): use
<fieldset>+<legend>em vez de label/for individual.Groups (Checkbox/Radio): use<fieldset>+<legend>instead of individual label/for.
Mapeamento de tokensToken mapping
| PropriedadeProperty | Token (Component) | Variável CSSCSS variable |
|---|---|---|
| root gap | component.form-field.gap.default | --ds-form-field-gap-default |
| label-row gap | component.form-field.label-row.gap.default | --ds-form-field-label-row-gap-default |
| label color | component.form-field.label.color.{default,disabled,readonly} | --ds-form-field-label-color-* |
| required color | component.form-field.required.color.default | --ds-form-field-required-color-default |
| helper color | component.form-field.helper.color.default | --ds-form-field-helper-color-default |
| error color | component.form-field.error.color.default | --ds-form-field-error-color-default |
| error gap | component.form-field.error.gap.default | --ds-form-field-error-gap-default |
| error icon | component.form-field.error.icon.{color,size,stroke-width}.default + component.form-field.error.icon-frame.padding-x.default | --ds-form-field-error-icon-*-default, --ds-form-field-error-icon-frame-padding-x-default |
| label typography | component.form-field.label.{font-size,line-height,font-weight}.default | --ds-form-field-label-*-default |
| helper/error typography | component.form-field.{helper,error}.{font-size,line-height,font-weight}.default | --ds-form-field-helper-*-default / --ds-form-field-error-*-default |
Esses tokens existem como contrato CSS-only do Form Field. Eles aliasam Semantic quando o valor já é uma decisão reutilizável e não criam um componente visual Form Field no Figma, conforme ADR-017.These tokens exist as the CSS-only Form Field contract. They alias Semantic when the value is already reusable and do not create a visual Form Field component in Figma, per ADR-017.
Classes CSSCSS classes
| ClasseClass | UsoUsage |
|---|---|
ds-field | Wrapper. Stack vertical com gap consistente.Wrapper. Vertical stack with consistent gap. |
ds-field__label-row | Linha do label + required (horizontal).Label + required row (horizontal). |
ds-field__label | Texto do label (14px medium).Label text (14px medium). |
ds-field__required | Asterisco visual (decorativo, aria-hidden).Visual asterisk (decorative, aria-hidden). |
ds-field__helper | Helper text (12px secundário).Helper text (12px secondary). |
ds-field__error | Linha de erro com ícone automático e mensagem. Visível apenas em --error.Error row with automatic icon and message. Visible only with --error. |
ds-field--error | Modificador: estado de erro. Mostra error message e aplica o contrato de erro ao controle associado.Modifier: error state. Shows error message and applies the error contract to the associated control. |
ds-field--no-label | Modificador: esconde label-row. Usar com aria-label no controle.Modifier: hides label-row. Pair with aria-label on control. |
ds-field--no-helper | Modificador: esconde helper.Modifier: hides helper. |