Skip to content

Input

A text field built on the native <input>, carrying the field composition every root form control shares.

Usage

<input is="root-input" type="text" data-label="Name" placeholder="Enter text…" />

The composition comes from fieldDecorator in @root/core, which Select, Textarea, Checkbox, Radio, Number, Range and the date/time pickers use as well: a native control cannot hold children, so a [field-wrapper] span carries the box - border, height, background - and anything that sits inside it, while the <label> and the hint are its siblings. The wrapper is always built, with or without a label.

<label>
  <span data-part="label">Name</span>
  <span field-wrapper>
    <input is="root-input" type="text" />
  </span>
  <span data-part="hint" aria-live="polite">…</span>
</label>

Anything measuring "the field" has to read the wrapper: the control itself reports the user agent's own box, which looks exactly like a regression and is not one.

Sizes

data-size takes small and large; the default is the medium step and has no value to write. Measured wrapper heights are 36, 40 and 44px; large also takes the next radius step, and the label text scales with the field.

<input is="root-input" type="text" data-size="large" data-label="Large" />
<input is="root-input" type="text" data-label="Medium" />
<input is="root-input" type="text" data-size="small" data-label="Small" />

The attribute stays on the control, where the author put it - the per-size branches reach it from the wrapper through :has(), because custom properties inherit downwards only and a token set on the control is invisible to its own parent.

Types

The type is the platform's, and so is everything that follows from it: the keyboard, the parsing and the constraint validation that feeds the error message.

<input is="root-input" type="email" data-label="Email" />
<input is="root-input" type="password" data-label="Password" />
<input is="root-input" type="search" data-label="Search" data-icon-end="caret-down" />
<input is="root-input" type="tel" data-label="Phone" />

Icons

data-icon-start and data-icon-end take a name from the root-icons font and place an <i is="root-icon"> inside the field, in flow beside the control. Clicking one fires root-input.icon.click with the icon's name; a click on the field itself does not.

<input is="root-input" type="text" data-label="Search" data-icon-start="check" data-icon-end="clipboard" />

Hint and error

data-hint holds the description and data-error the message: both are authored once, and data-invalid decides which one shows. One node carries both, wired to the control through aria-describedby and announced with aria-live="polite", and an invalid control also gets aria-invalid.

<input is="root-input" type="text" data-label="Username" data-hint="Three characters or more" />
<input
  is="root-input"
  type="email"
  required
  data-label="Email"
  data-hint="We'll never share it"
  data-error="That is not an email address"
  data-invalid="true"
/>

With no data-error the browser's own validationMessage stands in - already localized, and driven by required, type and the rest of native constraint validation. It stays current as the value changes.

<input is="root-input" type="email" required data-label="Email" data-invalid="true" />

The error paints the label and the hint in Danger 500 and leaves the border alone, which is what the component set draws. :user-invalid backs it, so a required-but-untouched field is not painted red before anything has been typed.

Required

required is the native attribute; the asterisk beside the label is drawn from it and hidden from assistive tech, which reads the control's own required instead.

<input is="root-input" type="text" required data-label="Full name" />

States

Hover moves the border to Neutral 500, focus to Default 500 - and focus draws no ring: the border moving is the indicator the design specifies, and two nested indicators are not. disabled repaints the box and takes the not-allowed cursor; readonly carries no styling of its own, the value being selectable and the field still focusable.

<input is="root-input" type="text" data-label="Tab to me" />
<input is="root-input" type="text" data-label="Disabled" value="Cannot edit" disabled />
<input is="root-input" type="text" data-label="Read-only" value="Copyable" readonly />

Inline layout

inline on the <label> lays the label text and the field on one row instead of stacking them. It has to sit on the label, which means authoring the label yourself - the generated one takes no attributes from the control.

<label inline>
  <span data-part="label">Quantity</span>
  <span field-wrapper>
    <input is="root-input" type="text" data-size="small" value="12" />
  </span>
</label>

No-autofill

Add no-autofill to opt a field out of native autocomplete/autocorrect/ autocapitalize/spellcheck and the common password-manager browser extensions (1Password, LastPass, Bitwarden) - none of which honor a single standard attribute.

<input is="root-input" type="text" no-autofill data-label="Coupon code" />

Palette

The field names no color attribute: it reads the ramps directly. The border is Neutral 400 at rest, Neutral 500 on hover and Default 500 on focus; the box is --root-c--background-color, the surface pole that mirrors across schemes; the error state is Danger 500; the disabled box is Neutral 100 on Neutral 200.

Attributes

Attribute Type Default Description
type string 'text' Any native input type; set by the component when absent
placeholder string - Placeholder text
data-size 'small' | 'large' medium Field height, radius and label scale
data-label string - Label text; the component builds the <label>
required boolean false Marks the field required and draws the asterisk
data-hint string - Description shown under the field
data-error string - Error message; falls back to validationMessage
data-invalid 'true' - Shows the error instead of the description
data-icon-start string - Icon name placed before the control
data-icon-end string - Icon name placed after the control
no-autofill boolean false Opt out of autocomplete and password managers
disabled boolean false Disables the input
readonly boolean false Read-only state
inline boolean false On the <label>: label and field on one row

Parts

Part Description
[field-wrapper] The field box; carries the border, height and background
[data-part="label"] The label text, holding the required marker
[data-part="required"] The decorative asterisk
[data-part="icon-start"] / [data-part="icon-end"] The generated icons
[data-part="hint"] The description / error node

Events

Namespaced as <component>.<event>, bubbling and cancelable; detail also carries the originating DOM event as originalEvent.

Event Detail Description
root-input.input { value } Fired on input
root-input.change { value } Fired on change
root-input.focus { value } Fired on focus
root-input.blur { value } Fired on blur
root-input.invalid { value } Fired when the control fails constraint validation
root-input.icon.click { icon } Fired when an icon inside the field is clicked
root-input.data-label.change { value, oldValue } Fired when the label text changes

Extends

HTMLInputElement - use with <input is="root-input">.