Skip to content

Forms

Structure

Wrap forms in .form for consistent styling. Use semantic elements:

<form class="form">
  <label>
    Label text
    <input type="text" placeholder="Placeholder" />
  </label>
  <footer>
    <button class="primary">Save</button>
    <button type="button">Cancel</button>
  </footer>
</form>

Inputs

Text

<label>
  Label text
  <input type="text" placeholder="Placeholder text" />
</label>

Select

<label>
  Choose an option
  <select>
    <option>Option one</option>
    <option>Option two</option>
  </select>
</label>

Textarea

<label>
  Description
  <textarea rows="3" placeholder="Enter details..."></textarea>
</label>

Range

Slider for a bounded numeric value.

<input type="range" min="0" max="100" value="60" />
slider.style.setProperty('--fill', `${percent}%`); // for WebKit support

Toggle

On/off toggle using role="switch" on a checkbox.

<label>
  <input type="checkbox" role="switch" />
  Enable notifications
</label>

Help text

Use <small> below inputs for additional guidance.

<label>
  API key
  <input type="password" placeholder="sk-..." aria-describedby="api-key-hint" />
  <small id="api-key-hint">Found in your provider's dashboard.</small>
</label>

Input groups

Button add-on

<div class="input-group">
  <input type="text" name="name" value="llama3.2:3b" />
  <button type="button">Download</button>
</div>

Status add-on

Use data-status="ok" for a green indicator dot.

<div class="input-group">
  <input type="text" name="name" value="llama3.2:3b" />
  <span data-status="ok" aria-label="Downloaded"></span>
</div>

Accessibility

Status add-ons without visible text must have aria-label.

Sections

Group related fields with <fieldset> and <legend>:

Connection settings
<fieldset>
  <legend>Connection settings</legend>
  <label>
    Host
    <input type="text" placeholder="localhost" />
  </label>
  <label>
    Port
    <input type="number" placeholder="5432" />
  </label>
</fieldset>

Layout

Use .row for inline field groups. Children get equal width by default.

<div class="row">
  <label>
    First name
    <input type="text" />
  </label>
  <label>
    Last name
    <input type="text" />
  </label>
</div>

Sizing modifiers

Class Purpose
.narrow Fixed 140px width
.shrink Natural width, no flex
<div class="row">
  <label>
    Host
    <input type="text" />
  </label>
  <label class="narrow">
    Port
    <input type="number" />
  </label>
</div>

Actions

Use <footer> for the button row at the end of a form:

<form class="form">
  <!-- fields -->
  <footer>
    <button class="primary">Save</button>
    <button type="button">Cancel</button>
  </footer>
</form>

Inline errors

Use .alert.error after the form actions for validation feedback:

<form class="form">
  <!-- fields -->
  <footer>
    <button class="primary">Submit</button>
  </footer>
  <div class="alert error" role="alert">Passphrases do not match.</div>
</form>