Input
hc-input is applied to the standard <input>, <select>, and
<textarea> elements. State (disabled, aria-invalid) lives on the
element itself, exactly as in plain HTML.
Also known as: text field, text box.
Basic HTML
Section titled “Basic HTML”<input class="hc-input" type="text" placeholder="Your name"><input class="hc-input" type="email" placeholder="you@example.com">
<select class="hc-input"> <option>One</option> <option>Two</option> <option>Three</option></select>
<textarea class="hc-input" rows="3" placeholder="Notes…"></textarea>data-size accepts sm, md (default), and lg.
<input class="hc-input" data-size="sm" placeholder="Small"><input class="hc-input" placeholder="Default"><input class="hc-input" data-size="lg" placeholder="Large">States
Section titled “States”<input class="hc-input" placeholder="Focus me to see the ring"><input class="hc-input" aria-invalid="true" value="not-an-email"><input class="hc-input" disabled value="Disabled value">The aria-invalid="true" attribute switches the border to the error
color and changes the focus-ring color. Pair it with a message via
hc-field.
Variants
Section titled “Variants”data-variant recolors the border as a validation cue —
success (green), warning (amber), error (red) — the same
vocabulary as select,
datepicker, and the
other form fields. (textarea takes the same attribute.)
<input class="hc-input" data-variant="success" value="Looks good"><input class="hc-input" data-variant="warning" value="Double-check this"><input class="hc-input" data-variant="error" value="Fix this">Prefer aria-invalid="true" over data-variant="error" for the error
case — it paints the same border and announces the state to assistive
tech. success / warning have no native attribute, so the variant is
the only way to express them. See the cross-component
Variants matrix.
Numeric fields
Section titled “Numeric fields”Inputs render digits as tabular figures by default
(font-variant-numeric: tabular-nums) — a steady rhythm while typing
digits, and width parity with table cells; text content is unaffected.
For amount-style fields, data-numeric end-aligns the value — the same
attribute and semantics as table
cells, logical so RTL flips free:
<label class="hc-field"> <span class="hc-field__label">Amount</span> <input class="hc-input" name="amount" data-numeric inputmode="decimal"></label>Grouped amounts
Section titled “Grouped amounts”For amount fields, add data-hc-format="number" — the auto-installed
installFormat() behavior keeps the display grouped and the wire
raw:
<label class="hc-field"> <span class="hc-field__label">Amount</span> <div class="hc-input-group"> <span class="hc-input-addon">¥</span> <input class="hc-input" name="amount" type="text" inputmode="numeric" data-numeric data-hc-format="number" value="1,234,567"> </div></label>- Blur normalizes fullwidth digits (
1234→1,234, NFKC) and groups per locale; focus shows the raw value again, so editing never fights separators (and the behavior needs no caret management). - The server always receives the raw value (
amount=1234567): the behavior rewrites the entry list in theformdataevent, which fires for both the htmx request (new FormData(form)) and the native submit. data-decimals="2"pads to a minimum number of fraction digits (display only — it never rounds);data-localeoverrides the grouping locale, defaulting to the closest[lang].- Render the initial value grouped server-side if it should look grouped before the first blur — the wire value stays raw either way.
Use type="text" + inputmode="numeric", not type="number":
type="number" rejects grouped values, drops leading zeros, and
changes the value on scroll — all wrong for amounts. Unparseable input
is left exactly as typed; the server stays the validator
(field-errors).
IME normalization
Section titled “IME normalization”data-hc-normalize (the auto-installed installNormalize()) rewrites
a control’s value on commit, so fullwidth leftovers from IME typing
self-correct without re-typing:
<input class="hc-input" name="sku" data-hc-normalize="ascii"><input class="hc-input" name="furigana" data-hc-normalize="kana">ascii— fullwidth ASCII → halfwidth (AB12→AB12, NFKC) and ideographic space → plain space. Put it only on fields whose wire format is ASCII (codes, identifiers, phone digits).kana— halfwidth kana → fullwidth (タロウ→タロウ) and hiragana → katakana (やまだ→ヤマダ), for furigana fields.
The rewrite runs in a capture-phase change listener, so htmx triggers
reading target.value already see the normalized value — with a
formdata safety net for values that never fired change.
Input masks
Section titled “Input masks”data-hc-mask (the auto-installed installMask()) renders fixed-format
codes as the user types — literals appear by themselves and fullwidth
input fills the slots:
<input class="hc-input" name="postal" inputmode="numeric" placeholder="123-4567" pattern="\d{3}-\d{4}" data-hc-mask="postal-jp">
<input class="hc-input" name="product" placeholder="AB-12" data-hc-mask="AA-##">- Tokens:
#digit,aletter,Aletter (upcased),*alphanumeric; every other character is a literal.postal-jpis an alias for###-####. Characters that fit no slot are dropped. - Typing
1234shows123-4(literals render lazily); Backspace and Delete hop a literal run and always consume a raw character, so the caret never sticks on a hyphen. - The submitted value is the displayed canonical form.
data-hc-mask-submit="raw"strips literals on the wire (servers that store 7-digit postal codes) — sameformdatamechanism as grouped amounts. - Mirror the mask with
pattern+placeholder: the behavior never blocks submission, so no-JS submits stay validatable.
Variable-width formats (Japanese landline numbers) have no fixed mask — leave those fields unmasked and normalize server-side.
Multi-value conditions
Section titled “Multi-value conditions”Business screens often need to ask about a list of identifiers — a
column of order numbers pasted out of a spreadsheet. A <textarea> is
the right control for that (it takes a paste of any size), and
installMultiValue() puts each line on the wire as its own value:
<textarea class="hc-input" name="f-buyer" data-hc-multi="lines">ZAB001000000ZAB001000001ZAB001000002</textarea>?f-buyer=ZAB001000000&f-buyer=ZAB001000001&f-buyer=ZAB001000002The split happens on the formdata event, the same hook
installFormat() uses — htmx’s new FormData(form) and a native submit
both fire it, so one listener covers both transports and nothing wraps
the network. Values are trimmed and de-duplicated; data-hc-multi="commas"
splits on commas too. A control emptied of everything contributes no
entry at all — an empty condition is not a condition.
Have the server accept the raw newline-joined value as well: without JavaScript the textarea submits exactly that, and splitting it server-side keeps the no-JS path working.
The datagrid-filter recipe documents what to do when the list outgrows a URL — a stored condition set addressed by id, which must never fail open.
htmx usage
Section titled “htmx usage”hc-input works with htmx like any other input. The live-search
recipe wires the input to send a request as the user types.
<input class="hc-input" type="search" name="q" placeholder="Search" data-hx-get="/items" data-hx-trigger="input changed delay:300ms, search" data-hx-target="#results" data-hx-swap="innerHTML">Accessibility
Section titled “Accessibility”- Always associate inputs with a label. Use
<label for="...">linked to the input’sid, or wrap the input in a<label>element. - For validation errors, set
aria-invalid="true"on the input and reference the message witharia-describedby. - Do not remove the focus outline. The component replaces the default
outline with a visible border + box-shadow ring driven by
--hc-color-focus-ring. - Prefer the native
disabledattribute when the input must not be interactive. Usearia-disabled="true"only when the control must remain focusable.
Theming tokens
Section titled “Theming tokens”Component tokens (in component.tokens.json):
| Token path | Purpose |
|---|---|
input.height / input.padding-x | Default size. |
input.radius | Border radius. |
input.bg / input.fg | Default colors. |
input.border / input.focus-border | Border in normal/focus state. |
input.success-border / input.warning-border / input.error-border | Border for data-variant="success" / "warning" / aria-invalid. |
input.placeholder | Placeholder color. |
input.disabled-bg | Background when disabled. |
input.{sm,lg}.{height,padding-x,font-size} | Size-specific overrides. |
CSS variables
Section titled “CSS variables”Show the generated CSS variables
--hc-input-height--hc-input-padding-x--hc-input-radius--hc-input-font-size--hc-input-bg | -fg | -border | -placeholder--hc-input-focus-border--hc-input-success-border | -warning-border | -error-border--hc-input-disabled-bg--hc-input-sm-height | -sm-padding-x | -sm-font-size--hc-input-lg-height | -lg-padding-x | -lg-font-size--hc-color-focus-ring--hc-color-errorRelated
Section titled “Related”- Field — pairs an input with a label and message.
- live-search recipe — input + htmx debounce + result region.
Used in recipes: Autosave · Live search · Postal address · Unsaved changes