Skip to content
Documentation

Forms

Input, Select, Textarea, Field

Text inputs, selects, text areas and native date and time pickers, with a Field wrapper that ties label, hint and error to the control, plus a spam-trap field.

CSS only components/input.css

Field

Wrap the control in a dsgn-field with a dsgn-label above it and an optional dsgn-hint below. Connect the hint with aria-describedby, so a screen reader reads it with the field.

We only use it for invoices.

An e-mail field with a label and a hint
<div class="dsgn-field">
  <label class="dsgn-label" for="email">E-mail</label>
  <input class="dsgn-input" id="email" type="email" autocomplete="email" aria-describedby="email-hint">
  <p class="dsgn-hint" id="email-hint">We only use it for invoices.</p>
</div>

Error

Set aria-invalid="true" on the control and give the hint data-intent="danger" with an icon. Say what is wrong and how to fix it, not only that something is wrong.

A company ID has 8 digits. This one has 4.

A field with an error message
<div class="dsgn-field">
  <label class="dsgn-label" for="ico">Company ID</label>
  <input class="dsgn-input" id="ico" inputmode="numeric" value="1234" aria-invalid="true" aria-describedby="ico-error">
  <p class="dsgn-hint" id="ico-error" data-intent="danger"><span class="dsgn-icon" aria-hidden="true">error</span>A company ID has 8 digits. This one has 4.</p>
</div>

Text area

The same class on a <textarea>. It is three lines tall unless rows says otherwise.

A text area
<div class="dsgn-field">
  <label class="dsgn-label" for="note">Note for the client</label>
  <textarea class="dsgn-input" id="note"></textarea>
</div>

With an icon

For an icon, the class moves to a wrapper and the control sits inside it. A control without a visible label needs an aria-label.

A search field with a leading icon
<div class="dsgn-input">
  <span class="dsgn-icon" aria-hidden="true">search</span>
  <input type="search" aria-label="Search invoices" placeholder="Search invoices">
</div>

Select

A select is the same wrapper with a chevron after the native <select>.

A select with a label
<div class="dsgn-field">
  <label class="dsgn-label" for="country">Country</label>
  <div class="dsgn-input">
    <select id="country">
      <option>Czechia</option>
      <option>Slovakia</option>
      <option>Germany</option>
    </select>
    <span class="dsgn-icon" aria-hidden="true">expand_more</span>
  </div>
</div>

Date and time

Use the native control: type="date", time, datetime-local or month. The browser brings its own calendar, keyboard entry and the local date format, and the picker follows light and dark mode. For a range, put two date fields side by side, each with its own label.

A date range
<div class="dsgn-cluster">
  <div class="dsgn-field">
    <label class="dsgn-label" for="from">From</label>
    <input class="dsgn-input" id="from" type="date" value="2026-11-01">
  </div>
  <div class="dsgn-field">
    <label class="dsgn-label" for="to">To</label>
    <input class="dsgn-input" id="to" type="date" value="2026-11-30">
  </div>
</div>

States

Hover, focus, aria-invalid="true", disabled and readonly are styled from the native state; there are no extra classes.

A disabled and a read-only field
<div class="dsgn-grid" data-cols="1" data-cols-tablet="2">
  <div class="dsgn-field">
    <label class="dsgn-label" for="plan">Plan</label>
    <input class="dsgn-input" id="plan" value="Team" disabled>
  </div>
  <div class="dsgn-field">
    <label class="dsgn-label" for="vat">VAT number</label>
    <input class="dsgn-input" id="vat" value="CZ12345678" readonly>
  </div>
</div>

Size

An input is exactly as tall as a button in the same density, so a field and a button line up in one row. The border is drawn inside, so it never adds to the height.

Accessibility

  • Every control needs a label: a <label for> or, when there is no room for one, an aria-label. A placeholder is not a label.
  • Set autocomplete and the right type or inputmode, so phones show the right keyboard and browsers can fill the field.
  • Link hints and errors with aria-describedby. Mark an invalid field with aria-invalid="true", not only with colour.

Usage

With icons, the class goes on a wrapper and the control sits inside:

Select is the same wrapper with a chevron:

Date and time: the native control, so the browser's own calendar, keyboard entry and locale format (12. 10. 2026 in Czech) come for free and work on every phone:

Also type="time", "datetime-local" and "month". The picker button sits at the end like a Select chevron (calendar_month / schedule glyph); the picker itself follows light / dark (color-scheme). A range is two date inputs in a Cluster, each with its own label (From / To).

States: :hover, focus (ring), aria-invalid="true", :disabled, [readonly]. Height = line-height + 2 × pad-y, identical to Button, so they line up in a row. The border is an inset box-shadow: it never adds to the height (Figma: stroke inside).

Details

Classes
.dsgn-input .dsgn-icon .dsgn-field .dsgn-label .dsgn-hint .dsgn-honeypot
Tokens used
--dsgn-border --dsgn-border-control --dsgn-border-width --dsgn-control-font-size --dsgn-control-gap --dsgn-control-height --dsgn-control-line-height --dsgn-control-pad-x --dsgn-control-pad-y --dsgn-danger-solid --dsgn-danger-text --dsgn-duration-fast --dsgn-ease --dsgn-focus --dsgn-focus-danger --dsgn-focus-offset --dsgn-focus-width --dsgn-font-sans --dsgn-font-size-body-s --dsgn-font-size-caption --dsgn-font-weight-medium --dsgn-font-weight-regular --dsgn-line-height-body-s --dsgn-line-height-caption --dsgn-neutral-subtle --dsgn-radius-l --dsgn-radius-m --dsgn-shadow-control --dsgn-space-4 --dsgn-surface-base --dsgn-surface-raised --dsgn-text --dsgn-text-disabled --dsgn-text-muted
In Figma
Input · Textarea · Field · Date input
Import alone
dsgn.systems/components/input.css