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.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.
PadrãoDefault
<div class="ds-select">
<select class="ds-select__field">
<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>
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 (semantic.size.control.sm) | semantic.typography.control.font-size.sm | semantic.space.control.padding-x.sm | 32px — meets WCAG 2.5.8 min (24px) |
Medium (--md) | 40px (semantic.size.control.md) | semantic.typography.control.font-size.md | semantic.space.control.padding-x.md | 40px |
Large (--lg) | 48px (semantic.size.control.lg) | semantic.typography.control.font-size.lg | semantic.space.control.padding-x.lg | 48px — meets AAA 44px target |
Com íconeWith icon
<div class="ds-select">
<span class="ds-select__icon material-symbols-outlined">language</span>
<select class="ds-select__field">
<option>English</option>
<option>Portuguese</option>
<option>Spanish</option>
</select>
<span class="ds-select__arrow"></span>
</div>
EstadosStates
Estados são controlados via pseudo-classes CSS (:hover, :focus-visible, [disabled]) e classes modificadoras para erro e somente leitura.States are handled via CSS pseudo-classes (:hover, :focus-visible, [disabled]) and modifier classes for error and readonly.
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>
</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>
| State | CSS trigger | Visual change | Token |
|---|---|---|---|
| Default | --- | Neutral border | semantic.border.default |
| Hover | :hover | Darker border | semantic.border.control-hover |
| Focused | :focus-visible | 2px outline ring, 2px offset | semantic.focus.ring.* |
| Error | .ds-select--error | Red border | semantic.feedback.error.bg-default |
| Disabled | [disabled] | Muted bg + fg, no pointer events | semantic.background.disabled |
| Readonly | .ds-select--readonly + [disabled] | No border change, full opacity text | semantic.background.disabled |
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
Mesmo conjunto de tokens do Input. O indicador de seta herda a cor do texto via currentColor.Same token set as Input. The arrow indicator inherits text color via currentColor.
| PropriedadeProperty | Token (semantic) | Variável CSSCSS variable |
|---|---|---|
| background | semantic.surface.default | --ds-surface-default |
| border (default) | semantic.border.default | --ds-border-default |
| border (hover) | semantic.border.control-hover | --ds-border-control-hover |
| border (error) | semantic.feedback.error.bg-default | --ds-feedback-error-background-default |
| text color | semantic.content.default | --ds-content-default |
| placeholder color | semantic.content.tertiary | --ds-content-tertiary |
| arrow color | Inherits via currentColor | --- |
| background (disabled) | semantic.background.disabled | --ds-background-disabled |
| text (disabled) | semantic.content.disabled | --ds-content-disabled |
| border-radius | semantic.radius.component | --ds-radius-md |
| focus ring | semantic.focus.ring.* | --ds-focus-ring-width, --ds-focus-ring-offset, --ds-focus-ring-color |
| height (sm) | semantic.size.control.sm | --ds-size-lg (32px) |
| height (md) | semantic.size.control.md | --ds-size-xl (40px) |
| height (lg) | semantic.size.control.lg | --ds-size-2xl (48px) |
| padding-x (sm) | semantic.space.control.padding-x.sm | --ds-space-md |
| padding-x (md) | semantic.space.control.padding-x.md | --ds-space-lg |
| padding-x (lg) | semantic.space.control.padding-x.lg | --ds-space-xl |
| font-size (sm) | semantic.typography.control.font-size.sm | --ds-control-font-size-sm |
| font-size (md) | semantic.typography.control.font-size.md | --ds-control-font-size-md |
| font-size (lg) | semantic.typography.control.font-size.lg | --ds-control-font-size-lg |
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__label-row | Linha horizontal com label e asterisco de obrigatórioHorizontal row with label and required asterisk |
ds-field__required | Asterisco * em feedback/error/default (decorativo, aria-hidden)Asterisk * in feedback/error/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/default ao lado do labelShows * in feedback/error/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 (34px) exceeds 24px minimum | ✓ |
| 2.5.5 Target Size (AAA) | Medium (47px) and Large (58px) meet 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.