HelperText

The little message under an input — a hint while you type, the verdict once a check runs. Four severity tones match Input's states so the field and its message always agree: neutral / valid / caution / invalid. Per ADR-0017, severity-toned helper text always pairs with an icon — color alone is never the signal.

For the canonical form-field composition (label + input + helper text with auto-linked ARIA via aria-describedby), use /text-field. Use HelperText on its own for one-off hints that don’t need a label association.

Variants

Four severity tones. Pass the icon as the first child — the pattern is flex and the icon aligns to the first line of text.

We’ll never share your email with anyone.

variant="neutral"
Default. A quiet hint in muted text — no icon needed (there's no severity to signal).

Username available.

variant="valid"
Success green. A check passed — say so plainly. Icon required.

This action can’t be undone.

variant="caution"
Warning bronze. A heads-up — not an error, but something you should know. Icon required.

Email format invalid — must include an @ sign.

variant="invalid"
Danger red. Validation failed. Icon required.

Paired with an Input

The whole point — HelperText sits below an Input and tells the user what’s happening with the value. Severity tones on HelperText pair with the matching state on Input so the field and its message read as one validated unit.

We’ll never share your email.

Idle field + neutral hint
The classic form-field shape. Nothing has been checked yet — the hint just tells you what the field expects.

Email format invalid — must include an @ sign.

Invalid field + invalid helper
Validation failed. Red border, red helper — the field and its message read as one unit, and the icon makes the call unambiguous.

Username available.

Valid field + valid helper
A check passed. Green border, green helper — the explicit “you’re good” signal.

Transferring ownership is irreversible.

Idle field + caution helper
The field isn’t wrong; the user just needs to know something before they submit. The field stays idle; the helper carries the caution tone.

Password match — live demo

The canonical use of the valid / invalid pair on Input and HelperText together. Edit either field and watch the message and border switch live.

At least 12 characters.

Password
Always neutral while typing; only the confirmation reflects match state.

Passwords match.

Confirm
Match — green border + green helper.

Props

variant
'neutral' | 'valid' | 'caution' | 'invalid'

Severity tone. Defaults to neutral. Severity-toned variants (valid, caution, invalid) require an icon child per ADR-0017.

children
ReactNode

The message text, optionally preceded by an icon. The pattern is flex; pass the icon as the first child to get baseline-aligned icon + text.

…native attrs
HTMLAttributes<HTMLParagraphElement>

All standard <p> attributes pass through. id is useful when wiring aria-describedby from a paired input — TextField does this automatically.

Usage

Neutral hinttsx
import { HelperText } from '@halo-compliance/ui'

<HelperText>
  We'll never share your email.
</HelperText>
Validation resulttsx
<HelperText variant="invalid">
  <CircleX /> Email format invalid.
</HelperText>
With Input + aria-describedbytsx
<Input
  type="email"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
  invalid={!!error}
  aria-describedby="email-helper"
/>
<HelperText
  id="email-helper"
  variant={error ? 'invalid' : 'neutral'}
>
  {error ? <><CircleX /> {error}</> : "We'll never share it."}
</HelperText>

Anti-patterns

  • Don’t carry severity by color alone. Every non-neutral variant requires an icon (ADR-0017). The icon is the unambiguous signal for color-blind users.
  • Don’t use HelperText for body content. It’s specifically a field-message text role — small, baseline-aligned, paired with an affordance. For inline prose use <Text variant="body-sm">.
  • Don’t put the icon inside a wrapper. The component’s flex layout aligns the icon to the first line of text. Wrapping the icon in a span breaks the alignment.
  • Don’t pair a valid HelperText with an idle field. The whole “validation passed” signal is the field+message together — if you can’t put the field in valid state, don’t put the message in valid tone.
  • /input — the field HelperText sits below. The severity tones here mirror Input’s valid / invalid states.
  • /text-field — the compound pattern that wraps Input + HelperText with auto-linked ARIA. Reach for that when you need the full form-field shape.
  • /tokens--patina-700, --caution-700, --rust-700 for the severity text colors.