Select
Menus Select permitem que usuários escolham uma opção de uma lista dropdown. Usa um padrão de wrapper com <select> nativo e um indicador de seta customizado.Select menus allow users to choose one option from a dropdown list. Uses a wrapper pattern with native <select> and a custom arrow indicator.
Quando usarWhen to use
AnatomiaAnatomy
.ds-select) — container com borda, radius.2 Ícone (opcional,
.ds-select__icon) — ícone à esquerda, decorativo.3 Campo (
.ds-select__field) — elemento nativo <select>.4 Seta (
.ds-select__arrow) — chevron customizado, pointer-events: none.5 Mensagem de erro (
.ds-field__error) — externa, via Form Field.6 Texto auxiliar (
.ds-field__helper) — externo, via Form Field.7 Label (
.ds-field__label) — externo, via Form Field.1 Wrapper (.ds-select) — container with border, radius.2 Icon (optional,
.ds-select__icon) — leading icon, decorative.3 Field (
.ds-select__field) — native <select> element.4 Arrow (
.ds-select__arrow) — custom chevron, pointer-events: none.5 Error message (
.ds-field__error) — external, via Form Field.6 Helper text (
.ds-field__helper) — external, via Form Field.7 Label (
.ds-field__label) — external, via Form Field.
PadrãoDefault
<div class="ds-field">
<div class="ds-field__label-row">
<label class="ds-field__label" for="select-default">Label</label>
</div>
<div class="ds-select">
<select class="ds-select__field" id="select-default"
aria-describedby="select-default-helper">
<option value="" disabled selected>Choose an option</option>
<option value="1">Option One</option>
<option value="2">Option Two</option>
<option value="3">Option Three</option>
</select>
<span class="ds-select__arrow"></span>
</div>
<span class="ds-field__helper" id="select-default-helper">Helper text</span>
</div>
TamanhosSizes
<div class="ds-select ds-select--sm">
<select class="ds-select__field">...</select>
<span class="ds-select__arrow"></span>
</div>
<div class="ds-select ds-select--md">
<select class="ds-select__field">...</select>
<span class="ds-select__arrow"></span>
</div>
<div class="ds-select ds-select--lg">
<select class="ds-select__field">...</select>
<span class="ds-select__arrow"></span>
</div>
| Size | Height | Font | Padding | Touch target |
|---|---|---|---|---|
Small (--sm) | 32px (component.select.height.sm) | component.select.text.font-size.sm | component.select.padding-x.sm / component.field.padding-y.sm | 32px — meets WCAG 2.5.8 min (24px) |
Medium (--md) | 40px (component.select.height.md) | component.select.text.font-size.md | component.select.padding-x.md / component.field.padding-y.md | 40px |
Large (--lg) | 48px (component.select.height.lg) | component.select.text.font-size.lg | component.select.padding-x.lg / component.field.padding-y.lg | 48px — meets AAA 44px target |
Com íconeWith icon
<div class="ds-select">
<i data-lucide="languages" class="ds-select__icon ds-icon"></i>
<select class="ds-select__field">
<option>English</option>
<option>Portuguese</option>
<option>Spanish</option>
</select>
<span class="ds-select__arrow"></span>
</div>
EstadosStates
No Figma, State representa apenas estados visuais exclusivos: Default, Hover, Focus e Disabled. Filled, Error e Read-only são propriedades separadas; Error combina com Default/Hover/Focus, e Read-only combina com Default/Focus.In Figma, State only represents exclusive visual states: Default, Hover, Focus, and Disabled. Filled, Error, and Read-only are separate properties; Error combines with Default/Hover/Focus, and Read-only combines with Default/Focus.
Error
<div class="ds-field ds-field--error">
<label class="ds-field__label" for="country">Country<span class="ds-field__required">*</span></label>
<div class="ds-select ds-select--error">
<select class="ds-select__field" id="country" aria-invalid="true" aria-describedby="country-error">
<option value="" disabled selected>Select a country</option>
<option>Brazil</option>
</select>
<span class="ds-select__arrow"></span>
</div>
<span class="ds-field__error" id="country-error">Please select a country.</span>
<span class="ds-field__helper">Choose one country from the list.</span>
</div>
Disabled
<div class="ds-select">
<select class="ds-select__field" disabled>
<option>Disabled option</option>
</select>
<span class="ds-select__arrow"></span>
</div>
Readonly
<!-- Native select has no readonly; use disabled + ds-select--readonly for styling -->
<div class="ds-select ds-select--readonly">
<select class="ds-select__field" disabled>
<option>Read-only value</option>
</select>
<span class="ds-select__arrow"></span>
</div>
| Figma property | Value / CSS trigger | Visual change | Token |
|---|---|---|---|
| State | Default | Neutral border | component.field.border-color.default |
| State | Hover / :hover | Darker border | component.field.border-color.hover |
| State | Focus / :focus-visible | 2px outline ring, component radius | component.field.border-color.focus, component.focus-ring.*, component.select.focus-ring.radius.default |
| State | Disabled / [disabled] | Muted bg + content, no pointer events | component.field.bg.disabled, component.field.value.color.disabled, component.field.placeholder.color.disabled |
| Error | True / .ds-select--error or .ds-field--error .ds-select | Red border and error focus ring | component.field.border-color.error, component.focus-ring.color.error |
| Error + State | True + Hover / .ds-select--error:hover | Darker red border | component.field.border-color.error-hover |
| Read-only | True / .ds-select--readonly + [disabled] | Readonly bg and content treatment; native select has no real readonly focus | component.field.bg.readonly, component.field.value.color.readonly |
Boas práticasBest practices
<option value="" disabled selected>Choose a country</option>.Include a disabled placeholder option: <option value="" disabled selected>Choose a country</option>.Diretrizes de conteúdoContent guidelines
| RegraRule | ExemploExample |
|---|---|
| Placeholder: "Choose a..." or "Select a..." in sentence case | "Choose a country" -- not "CHOOSE A COUNTRY" or "Country" |
| Option text should be concise and consistent | "Brazil", "United States" -- not "The Federative Republic of Brazil" |
| Don't mix option types | All countries, or all languages -- not countries mixed with regions |
| Use sentence case for options | "Credit card" -- not "Credit Card" or "CREDIT CARD" |
Always pair with a <label> | <label for="country">Country</label> |
Mapeamento de tokensToken mapping
Select consome tokens Component no CSS e nos bindings do Figma. Esses tokens documentam o contrato público do componente e aliasam Semantic quando o valor é reutilizável.Select consumes Component tokens in CSS and Figma bindings. These tokens document the component's public contract and alias Semantic when the value is reusable.
| PropriedadeProperty | Token Component | Variável CSSCSS variable |
|---|---|---|
| bg | component.field.bg.{default|focus|filled|error|disabled|readonly} | --ds-field-bg-* |
| border color | component.field.border-color.{default|hover|focus|filled|error|disabled|readonly} | --ds-field-border-color-* |
| border width | component.field.border-width | --ds-field-border-width |
| content color | component.field.value.color.{default|disabled|readonly} | --ds-field-value-color-* |
| placeholder color | component.field.placeholder.color.{default|disabled} | --ds-field-placeholder-color-* |
| chevron | component.field.icon.{color|size|stroke-width}.* + component.field.icon-frame.padding-x.default | --ds-field-icon-*, --ds-field-icon-frame-padding-x-default |
| height | component.select.height.{sm|md|lg} | --ds-select-height-* |
| gap | component.field.gap.{sm|md|lg} | --ds-field-gap-* |
| padding | component.select.padding-x.* / component.field.padding-y.* | --ds-select-padding-x-* / --ds-field-padding-y-* |
| radius | component.field.radius | --ds-field-radius |
| focus ring | component.focus-ring.* + component.select.focus-ring.radius.default | --ds-focus-ring-* + --ds-select-focus-ring-radius-default |
| typography | component.field.text.* + component.form-field.{label|helper|required}.* | --ds-field-text-*, --ds-form-field-* |
Classes CSSCSS classes
| ClasseClass | DescriçãoDescription |
|---|---|
ds-select | Elemento wrapper do selectWrapper element for the select |
ds-select__field | O elemento nativo <select>The native <select> element |
ds-select__arrow | Indicador de seta dropdown customizadoCustom dropdown arrow indicator |
ds-select__icon | Ícone à esquerda dentro do wrapperLeading icon inside the wrapper |
ds-select--sm | Tamanho pequeno (altura 32px)Small size (32px height) |
ds-select--md | Tamanho médio (altura 40px, padrão)Medium size (40px height, default) |
ds-select--lg | Tamanho grande (altura 48px)Large size (48px height) |
ds-select--error | Estado de erro com borda vermelhaError state with red border |
ds-select--disabled | Estado desabilitado (ou use o nativo disabled)Disabled state (or use native disabled) |
ds-select--readonly | Estado visual somente leitura (combine com o nativo disabled)Readonly visual state (combine with native disabled) |
ds-field__error | Linha de erro com ícone automático e mensagem (exibida quando ds-field--error está definido)Error row with automatic icon and message (shown when ds-field--error is set) |
ds-field__label-row | Linha horizontal com label e asterisco de obrigatórioHorizontal row with label and required asterisk |
ds-field__required | Asterisco * em feedback/error/content/default (decorativo, aria-hidden)Asterisk * in feedback/error/content/default (decorative, aria-hidden) |
ds-field--no-label | Oculta o label row quando Show Label = falseHides the label row when Show Label = false |
ds-field--no-helper | Oculta o helper text quando Show Helper Text = falseHides helper text when Show Helper Text = false |
Propriedades FigmaFigma properties
| PropriedadeProperty | TipoType | PadrãoDefault | DescriçãoDescription |
|---|---|---|---|
Show Label | Boolean | true | Exibe ou oculta o label row (incluindo asterisco de obrigatório)Shows or hides the label row (including required asterisk) |
Label | Text | "Rótulo" | Texto do label (label/md)Label text (label/md) |
Required | Boolean | false | Exibe o asterisco * em feedback/error/content/default ao lado do labelShows * in feedback/error/content/default next to the label |
Show Helper Text | Boolean | true | Exibe ou oculta o texto auxiliar abaixo do controleShows or hides the helper text below the control |
Helper Text | Text | "Texto auxiliar" | Anotação em caption/sm (content/secondary)Caption/sm annotation (content/secondary) |
Interação por tecladoKeyboard interaction
| TeclaKey | AçãoAction |
|---|---|
Tab | Move o foco para o selectMoves focus to the select |
Space / Enter | Abre o dropdown (nativo do navegador)Opens the dropdown (browser-native) |
Arrow Up / Arrow Down | Navega entre as opçõesNavigates options |
Escape | Fecha o dropdownCloses dropdown |
| Any letter | Pula para a primeira opção que começa com aquela letraJumps to first option starting with that letter |
Todas as interações de teclado são tratadas nativamente pelo elemento <select>. Nenhum JavaScript customizado é necessário. Selects desabilitados são removidos da ordem de tabulação automaticamente.All keyboard interactions are handled natively by the <select> element. No custom JavaScript is required. Disabled selects are removed from tab order automatically.
AccessibilityAccessibility
| Critério WCAGWCAG criterion | RequisitoRequirement | Status |
|---|---|---|
| 2.4.11 Focus Appearance (AA) | Focus ring 2px + 2px gap, contrast >= 3:1 against adjacent colors | ✓ |
| 2.5.8 Target Size min (AA) | Smallest size (32px) exceeds 24px minimum | ✓ |
| 2.5.5 Target Size (AAA) | Large (48px) meets 44px touch target | ✓ |
| 1.4.3 Contrast (AA) | Text and arrow contrast >= 4.5:1 against background. Disabled exempt. | ✓ |
| 4.1.2 Name, Role, Value (A) | Use native <select> element. Associate with <label> via for/id. | ✓ |
| 1.3.1 Info and Relationships (A) | Error states use aria-invalid="true" and aria-describedby. | ✓ |
| 1.4.1 Use of Color (A) | Error state has visible red border + text message, not just color. | ✓ |
.ds-select__arrow) usa pointer-events: none -- é puramente decorativa e não interfere com interação de clique ou teclado.O
<select> nativo não possui atributo readonly. O DS usa disabled combinado com a classe ds-select--readonly para diferenciação visual. Diferente de disabled, readonly deve comunicar visualmente que o valor está definido mas não é editável.The custom arrow (.ds-select__arrow) uses pointer-events: none -- it is purely decorative and does not interfere with click or keyboard interaction.Native
<select> has no readonly attribute. The DS uses disabled combined with the ds-select--readonly class for visual differentiation. Unlike disabled, readonly should visually communicate that the value is set but not editable.
Required = true, adicione aria-required="true" no <select> — o asterisco visual (.ds-field__required) é decorativo (aria-hidden="true"). Quando Show Label = false, use aria-label no <select>. O Helper Text deve ser vinculado via aria-describedby.When Required = true, add aria-required="true" to the <select> — the visual asterisk (.ds-field__required) is decorative (aria-hidden="true"). When Show Label = false, use aria-label on the <select>. Helper Text should be linked via aria-describedby.