Select

A native dropdown for picking from a fixed list of options.

Basic

Add .select to any <select>. The field shape matches .input and .textarea so the three line up in a form row. The chevron and appearance reset come from the class. Pair with a <label> tied via for/id so the field is reachable.

<div class="w-100" style="max-width: 24rem;">
  <label for="basicSelect" class="form-label">Country</label>
  <select class="select" id="basicSelect">
    <option selected>Pick one</option>
    <option value="id">Indonesia</option>
    <option value="my">Malaysia</option>
    <option value="sg">Singapore</option>
  </select>
</div>

Sizes

Three sizes match the input scale. Add .select--sm or .select--lg.

<div class="demo-stack w-100" style="max-width: 24rem;">
  <select class="select select--sm" aria-label="Small select">
    <option selected>Small</option>
    <option value="1">One</option>
    <option value="2">Two</option>
  </select>
  <select class="select" aria-label="Default select">
    <option selected>Default</option>
    <option value="1">One</option>
    <option value="2">Two</option>
  </select>
  <select class="select select--lg" aria-label="Large select">
    <option selected>Large</option>
    <option value="1">One</option>
    <option value="2">Two</option>
  </select>
</div>

Option groups

Wrap related options in <optgroup> to label sections inside the menu.

<div class="w-100" style="max-width: 24rem;">
  <label for="groupedSelect" class="form-label">City</label>
  <select class="select" id="groupedSelect">
    <option selected>Pick one</option>
    <optgroup label="Indonesia">
      <option value="jkt">Jakarta</option>
      <option value="bdg">Bandung</option>
      <option value="sby">Surabaya</option>
    </optgroup>
    <optgroup label="Malaysia">
      <option value="kul">Kuala Lumpur</option>
      <option value="pen">Penang</option>
    </optgroup>
  </select>
</div>

Multiple

Add the multiple attribute to let users select several options. The chevron drops and the control grows past the default height to render the list inline.

<div class="w-100" style="max-width: 24rem;">
  <label for="multipleSelect" class="form-label">Tags</label>
  <select class="select" id="multipleSelect" multiple aria-label="Multiple select">
    <option value="bug">Bug</option>
    <option value="docs" selected>Docs</option>
    <option value="feat" selected>Feature</option>
    <option value="qa">QA</option>
    <option value="perf">Performance</option>
  </select>
</div>

Size attribute

Set size to render a fixed-row list instead of a dropdown. Useful when the option count is small enough to show inline.

<div class="w-100" style="max-width: 24rem;">
  <select class="select" size="4" aria-label="Size 4 select">
    <option selected>Pick one</option>
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
    <option value="4">Four</option>
  </select>
</div>

Helper text

Use .form-text below the field for short hints. Wire it to the select with aria-describedby so screen readers announce it.

You can change this later from billing.
<div class="w-100" style="max-width: 24rem;">
  <label for="planSelect" class="form-label">Plan</label>
  <select class="select" id="planSelect" aria-describedby="planHelp">
    <option selected>Pick one</option>
    <option value="free">Free</option>
    <option value="pro">Pro</option>
    <option value="team">Team</option>
  </select>
  <div id="planHelp" class="form-text">You can change this later from billing.</div>
</div>

Disabled

Add disabled to block interaction and dim the field. Individual <option> elements take the same attribute.

<div class="demo-stack w-100" style="max-width: 24rem;">
  <select class="select" disabled aria-label="Disabled select">
    <option selected>Disabled</option>
  </select>
  <select class="select" aria-label="Select with disabled option">
    <option selected>Pick a plan</option>
    <option value="free">Free</option>
    <option value="pro" disabled>Pro (sold out)</option>
    <option value="team">Team</option>
  </select>
</div>

Browser validation

Pair required with a placeholder <option value=""> so the user has to pick something with a real value. The native :user-invalid pseudo fires after the user interacts with the field, and clears the moment a valid option is selected.

Hit Submit without picking to trigger :user-invalid. Pick any option and the red clears on its own.
<form class="demo-stack w-100" style="max-width: 24rem;" onsubmit="event.preventDefault()">
  <div>
    <label for="reqPlan" class="form-label">Plan</label>
    <select class="select" id="reqPlan" required>
      <option value="" selected>Pick a plan</option>
      <option value="free">Free</option>
      <option value="pro">Pro</option>
      <option value="team">Team</option>
    </select>
    <div class="form-text">Hit Submit without picking to trigger <code>:user-invalid</code>. Pick any option and the red clears on its own.</div>
  </div>
  <button type="submit" class="btn btn--primary">Submit</button>
</form>

Server validation

Set aria-invalid="true" from your form library. The attribute is sticky; Stisla just paints while it's present. Pair with a .form-text--error message tied via aria-describedby.

This plan isn't available in your region.
<div class="demo-stack w-100" style="max-width: 24rem;">
  <div>
    <label for="srvPlan" class="form-label">Plan</label>
    <select
      class="select"
      id="srvPlan"
      aria-invalid="true"
      aria-describedby="srvPlanError">
      <option value="">Pick a plan</option>
      <option value="free" selected>Free</option>
      <option value="pro">Pro</option>
      <option value="team">Team</option>
    </select>
    <div id="srvPlanError" class="form-text form-text--error">This plan isn't available in your region.</div>
  </div>
</div>

The red stays even after you change the value. Your code removes aria-invalid when the field's error state resolves.

Customization

Nine variables retune .select without touching component CSS. Eight mirror the input surface under the --select- prefix so the chevron, padding, height, and so on can shift independently. The ninth is the chevron SVG itself.

Variable Default Use
--select-radius var(--st-input-radius, var(--st-radius)) Corner radius; sizes pull --st-radius-sm / -lg
--select-height 2.25rem Single-line height; sm and lg reassign this. Multi-line variants (multiple, size) treat this as a min-height floor
--select-padding-x calc(0.625rem * var(--st-density)) Horizontal padding; sm and lg reassign this
--select-font-size 0.875rem Text size; sm and lg reassign this
--select-bg var(--st-surface) Background
--select-color var(--st-foreground) Text color
--select-border var(--st-border) Border color; validation hooks flip this to --st-danger
--select-placeholder var(--st-muted-foreground) Placeholder text color (selects don't render a true placeholder, but the var is part of the shared field surface)
--select-indicator SVG data URL (chevron) Native chevron painted in the inline-end padding well; multiple and size drop it. Override with a different SVG to retheme the chevron

The chevron SVG carries a literal stroke color (one per theme) because data: URLs can't read CSS variables. To recolor the chevron, replace the whole URL with one whose stroke matches the new color.