Forms

stable

Form element styles ship as bare element selectors — no extra classes required on input, textarea, or select. All styles consume Tier-3 --input-* tokens so the full form palette can be rethemed in one place without touching semantic tokens.

Elements

Apply the styles by including farn-components.css. Every input, textarea, and select on the page is styled automatically. Use .form-field to group a label with its control.

<div class="form-field">
  <label for="name">Name</label>
  <input type="text" id="name" placeholder="Jane Smith" />
</div>

<div class="form-field">
  <label for="role">Role</label>
  <select id="role">
    <option value="">Choose a role</option>
    <option value="admin">Admin</option>
  </select>
</div>

<div class="form-field">
  <label for="bio">Bio</label>
  <textarea id="bio"></textarea>
</div>

States

Hover darkens the background to --input-bg-active. Focus adds a coloured border and a soft glow ring driven by --input-focus-shadow. The global :focus-visible outline from base.css is suppressed in favour of the token-driven focus ring. Error state can be set either via aria-invalid="true" on the input (preferred — screen readers announce the invalid state automatically) or via .form-field--error on the wrapper (colours the label and hint text too). Disabled inputs reduce opacity to --input-disabled-opacity and suppress pointer events.

Enter a valid email address.
<!-- Error via ARIA attribute — announced by screen readers -->
<input type="email" aria-invalid="true" value="bad@" />

<!-- Error via wrapper — colours label and hint text too -->
<div class="form-field form-field--error">
  <label for="email">Email</label>
  <input type="email" id="email" aria-invalid="true" value="bad@" />
  <span class="form-hint">Enter a valid email address.</span>
</div>

<!-- Disabled -->
<input type="text" disabled value="Cannot edit" />

Wrapper & hint text

.form-field is a flex column that stacks label, control, and optional hint text with consistent spacing. .form-hint renders helper or error text below the control in a smaller, secondary colour.

3–24 characters, letters and underscores only.
Enter a valid email address.
<div class="form-field">
  <label for="username">Username</label>
  <input type="text" id="username" />
  <span class="form-hint">3–24 characters.</span>
</div>

<!-- Error variant — hint text turns red automatically -->
<div class="form-field form-field--error">
  <label for="email">Email</label>
  <input type="email" id="email" aria-invalid="true" />
  <span class="form-hint">Enter a valid email address.</span>
</div>

Token reference

All --input-* tokens are defined in tokens/components.css.

TokenDefaultPurpose
--input-radiusvar(--radius-md)Border radius for all form controls
--input-bgvar(--color-bg-panel)Idle background
--input-bg-activevar(--color-bg-inset)Hover and focus background
--input-bordertransparentIdle border colour (invisible by default)
--input-border-focusvar(--color-accent)Focus border colour
--input-border-errorvar(--color-error)Error border colour
--input-focus-shadow0 0 0 3px color-mix(in srgb, var(--color-accent) 15%, transparent)Focus glow ring
--input-textvar(--color-text)Input text colour
--input-placeholdervar(--color-text-tertiary)Placeholder text colour
--input-disabled-opacity0.4Opacity applied to disabled controls

Overriding tokens

Override tokens on a scoped selector to retheme form controls in one section without affecting the rest of the page. Because --input-* tokens sit above the semantic layer, changes here have no effect on other elements that share the same semantic colour.

/* Tighter radius for a dense data-entry panel */
.data-panel {
  --input-radius: var(--radius-sm);
}

/* Always show a visible idle border in a section with no panel background */
.flat-form {
  --input-bg:     transparent;
  --input-border: var(--color-border);
}

Anatomy

<!-- Minimal — element selector handles all styling -->
<input type="text" placeholder="…" />

<!-- With label -->
<label for="x">Label</label>
<input type="text" id="x" />

<!-- Full wrapper pattern -->
<div class="form-field">
  <label for="y">Label</label>
  <input type="text" id="y" />
  <span class="form-hint">Helper text.</span>
</div>