Input

A text-like form field for any <input>.

Basic

Add .input to any <input>. Pair with a <label> tied via for/id so the field is reachable. The same shape extends to Select and Textarea so they line up in a form row.

<div class="w-100" style="max-width: 24rem;">
  <label for="basicInput" class="form-label">Email</label>
  <input type="email" class="input" id="basicInput" placeholder="you@example.com" />
</div>

Sizes

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

<div class="demo-stack w-100" style="max-width: 24rem;">
  <input type="text" class="input input--sm" placeholder="Small" />
  <input type="text" class="input" placeholder="Default" />
  <input type="text" class="input input--lg" placeholder="Large" />
</div>

Input types

The class applies to every text-like input: text, email, password, number, search, tel, url, date, time.

<div class="demo-stack w-100" style="max-width: 24rem;">
  <input type="email" class="input" placeholder="email" />
  <input type="password" class="input" placeholder="password" value="hunter2" />
  <input type="number" class="input" placeholder="number" />
  <input type="search" class="input" placeholder="search" />
  <input type="date" class="input" />
</div>

Helper text

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

At least 8 characters, one number.
<div class="w-100" style="max-width: 24rem;">
  <label for="passwordInput" class="form-label">Password</label>
  <input type="password" class="input" id="passwordInput" aria-describedby="passwordHelp" />
  <div id="passwordHelp" class="form-text">At least 8 characters, one number.</div>
</div>

Browser validation

Pair native constraint attributes (required, type="email", pattern, min, max) with the :user-invalid pseudo. The browser fires it after the user interacts with the field, and clears it the moment the value satisfies the constraints. Use for inline validation where the field owns its own state.

Hit Submit to trigger :user-invalid. Enter a valid email and the red clears on its own.
<form class="demo-stack w-100" style="max-width: 24rem;" onsubmit="event.preventDefault()">
  <div>
    <label for="reqEmail" class="form-label">Email</label>
    <input type="email" class="input" id="reqEmail" required placeholder="you@example.com" />
    <div class="form-text">Hit Submit to trigger <code>:user-invalid</code>. Enter a valid email 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. Frameworks like React Hook Form, Formik, vee-validate, and Rails / Django / Laravel form helpers manage this based on their own error state. Pair with a .form-text--error message tied via aria-describedby so the error reaches assistive tech.

Please enter a valid email address.
<div class="demo-stack w-100" style="max-width: 24rem;">
  <div>
    <label for="srvEmail" class="form-label">Email</label>
    <input
      type="email"
      class="input"
      id="srvEmail"
      value="not-an-email"
      aria-invalid="true"
      aria-describedby="srvEmailError" />
    <div id="srvEmailError" class="form-text form-text--error">Please enter a valid email address.</div>
  </div>
</div>

The red stays even after you edit the field. Stisla just paints while the attribute is present; your code removes it when the field's error state resolves.

Disabled & readonly

disabled blocks interaction and dims the field. readonly keeps the value reachable for copy but rejects edits. The bg shifts a tier to signal that you can select the value but you can't edit it.

<div class="demo-stack w-100" style="max-width: 24rem;">
  <input type="text" class="input" value="Disabled" disabled />
  <input type="text" class="input" value="Readonly" readonly />
</div>

Plain text

Swap .input for .input--plaintext to render a readonly value as bare text. No border and no background, but it still aligns with neighboring inputs in a form row. Pair with readonly on the input.

<div class="w-100" style="max-width: 24rem;">
  <label for="plainEmail" class="form-label">Email</label>
  <input type="email" readonly class="input--plaintext" id="plainEmail" value="you@example.com" />
</div>

Color picker

Add .input to any <input type="color">. The type selector handles the shape, no modifier class needed. The field collapses to a small swatch (no full-width stretch) and the native chip wears the field's inner radius (outer minus padding, V3.md §3.4) so the swatch and the frame share a center.

<div class="w-100" style="max-width: 24rem;">
  <label for="brandColor" class="form-label">Brand</label>
  <input type="color" class="input" id="brandColor" value="#3b82f6" />
</div>

File input

The same class styles type="file". The selector button sits as a small inset chip inside the field (same shape as a .btn--sm.btn--neutral) and the filename trails after it.

<div class="w-100" style="max-width: 24rem;">
  <label for="fileInput" class="form-label">Upload</label>
  <input type="file" class="input" id="fileInput" />
</div>

Customization

Eight variables retune .input without touching component CSS. Override on the field itself, on a parent scope, or on :root. The cascade scopes the change. The same surface appears on .select and .textarea under their own prefix so each field component can be tuned independently.

Variable Default Use
--input-radius var(--st-input-radius, var(--st-radius)) Corner radius (per-component override via --st-input-radius; sizes pull --st-radius-sm / -lg)
--input-height 2.25rem Single-line height; sm and lg reassign this. Multiplied by --st-density at the component level
--input-padding-x calc(0.625rem * var(--st-density)) Horizontal padding; sm and lg reassign this
--input-font-size 0.875rem Text size; sm and lg reassign this
--input-bg var(--st-surface) Background; readonly shifts a tier
--input-color var(--st-foreground) Text color
--input-border var(--st-border) Border color; validation hooks flip this to --st-danger
--input-placeholder var(--st-muted-foreground) Placeholder text color