Text Input
A native <input> or <textarea> styled via a single class. Same .x-text-input covers three usages:
- Bare input β
<input class="unset x-text-input" /> - Bare textarea β
<textarea class="unset x-text-input" /> - Wrapper β
<div class="x-text-input">with one input/textarea and any number of affix elements
Mode is selected by what the element is (:is(input, textarea)) and what it contains (:has(> input) β horizontal row, :has(> textarea) β vertical column). No marker class on affixes, no data-side. Source order is render order.
Install
import "elements-kit/ui/styles.css";
import "elements-kit/ui/styles/palette/gray.css";import "elements-kit/ui/styles/neutral/gray.css";import "elements-kit/ui/styles/palette/black-alpha.css";import "elements-kit/ui/styles/palette/mint.css";import "elements-kit/ui/styles/accent/mint.css";
import "elements-kit/ui/text-input/text-input.css";API
<!-- Bare input --><input class="unset x-text-input" data-size="2" data-variant="surface" />
<!-- Bare textarea --><textarea class="unset x-text-input" data-size="2" data-variant="surface" />
<!-- Input wrapper β horizontal: leading/trailing affixes --><div class="x-text-input" data-size="2" data-variant="surface"> <SearchIcon /> <input class="unset" placeholder="Searchβ¦" /> <kbd>βK</kbd></div>
<!-- Textarea wrapper β vertical: top/bottom affixes --><div class="x-text-input" data-size="2" data-variant="surface"> <div>Markdown</div> <textarea class="unset" /> <div>0 / 280</div></div>| Attribute | Values |
|---|---|
data-variant | surface (default), soft |
data-size | 1, 2 (default), 3 |
data-accent (on a parent or the element) | any imported color scale |
disabled / readonly | native β dims background, switches cursor |
Sizing
data-size | input height | textarea min-height | radius |
|---|---|---|---|
1 | --space-5 | --space-8 | max(--radius-2, --radius-pill) |
2 (default) | --space-6 | --space-9 | max(--radius-2, --radius-pill) |
3 | --space-7 | 80px | max(--radius-3, --radius-pill) |
Variants
surface(default) β--color-surfacebackground, 1px--neutral-a7border,--focus-8focus ring.softβ--accent-a3background, no border,--accent-8focus ring.
Wrapper anatomy
Inside the wrapper, document order chooses where an affix lands.
<div class="x-text-input"> <span>A</span> <!-- before input β leading (left for input, top for textarea) --> <input class="unset" /> <span>B</span> <!-- after input β trailing (right for input, bottom for textarea) --></div>- The wrapped
<input>/<textarea>has its background, border, padding, and outline stripped β the wrapper owns the chrome. - Affixes get
display: flex; align-items: center; cursor: textand a negative margin that extends them flush with the wrapper edge. - Focus-within outline wraps the whole container, so a focus ring lands once on the row/column rather than on the inner control.
Clicking an affix focuses the input because the affix carries cursor: text and the input remains the only focusable child in the row/column.
States
- Disabled / read-only β
var(--neutral-a11)text, dimmed background. When the input is empty (:placeholder-shown), cursor becomesvar(--cursor-disabled)and propagates to affixes inside the wrapper. - Autofill β Chromeβs yellow autofill background is suppressed via
background-clip: textwhile preserving the colored text.
Accessibility
- Pair with a
<label>(wrapping orfor=). The wrapper does not own the label. - The wrapper is a presentational container; the
<input>/<textarea>keeps all native semantics, form participation, and ARIA. aria-invalid="true"on the input/textarea is yours to apply for error states.