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.
<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.
<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.
<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.