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">.