Text Field
Self-contained label, input, and help/error text in one form-associated Web Component. Participates in native forms via ElementInternals and emits a composed change event.
Preview
<esa-text-field label="Full name" placeholder="Jane Doe" help-text="As it appears on the permit."></esa-text-field> Types
The type attribute maps straight to the native input type.
<esa-text-field label="Email" type="email" placeholder="you@example.com"></esa-text-field>
<esa-text-field label="Password" type="password"></esa-text-field>
<esa-text-field label="Count" type="number"></esa-text-field> Sizes
<esa-text-field label="Extra small" size="xs" placeholder="Extra small"></esa-text-field>
<esa-text-field label="Small" size="sm" placeholder="Small"></esa-text-field>
<esa-text-field label="Medium" size="md" placeholder="Medium"></esa-text-field>
<esa-text-field label="Large" size="lg" placeholder="Large"></esa-text-field> States
<esa-text-field label="Required" required placeholder="Cannot be empty"></esa-text-field>
<esa-text-field label="With error" error-text="This field is required." required></esa-text-field>
<esa-text-field label="Disabled" disabled value="Read only"></esa-text-field> Form participation
The element is form-associated. Submitting logs the FormData entry for contact.
<form>
<esa-text-field label="Contact" name="contact" placeholder="Type then submit"></esa-text-field>
<button type="submit">Submit</button>
</form> API
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | '' | Visible label rendered above the input. |
type | 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'text' | Native input type. |
value | string | '' | Current value; mirrored to the form on every input. |
placeholder | string | '' | Placeholder shown when empty. |
help-text | string | '' | Helper text shown below the input. |
error-text | string | '' | Error message; replaces help text and turns the field red when present. |
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Control size. |
required | boolean | false | Marks the field required and renders an asterisk on the label. |
disabled | boolean | false | Disables interaction. |
Events
| Event | Type | Default | Description |
|---|---|---|---|
change | CustomEvent<{ value: string }> | — | Fired on every input. Composed and bubbling; detail.value is the current string. |
Accessibility
- Renders a native
<input>associated to its<label>viafor/id. - Sets
aria-invalid="true"whileerror-textis present. - The required asterisk carries an
aria-label="required". - Visible focus ring via
--focus-ring-*tokens.
Theming surface
Every public token this component reads, extracted from its source at build time.
The Resolves to column walks each token's real lineage —
component → semantic → primitive → raw value — so you
can see exactly where re-pointing it lands. A spoke re-skins by overriding these in
its theme-<slug>.css; component internals are never edited. No
hook for what you need? File it with /request-lego.
| Token | Tier | Resolves to |
|---|---|---|
--focus-ring-color | component | #65ba74 via --color-border-focus → --color-grass-8 |
--focus-ring-width | component | 2px |
--form-bg | component | #fcfcfc via --color-surface → --color-gray-1 |
--form-bg-disabled | component | #f0f0f0 via --color-disabled-bg → --color-gray-3 |
--form-border-color | component | #cecece via --color-border → --color-gray-7 |
--form-border-color-error | component | #e5484d via --color-danger → --color-red-9 |
--form-border-color-focus | component | #65ba74 via --color-border-focus → --color-grass-8 |
--form-border-width | component | 1px |
--form-error-color | component | #ce2c31 via --color-danger-strong → --color-red-11 |
--form-font-size-lg | component | clamp(0.875rem, 0.77rem + 0.52vw, 1.125rem) via --type-size-300 |
--form-font-size-md | component | clamp(0.75rem, 0.66rem + 0.44vw, 0.9375rem) via --type-size-200 |
--form-font-size-sm | component | clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) via --type-size-100 |
--form-font-size-xs | component | clamp(0.5rem, 0.44rem + 0.3vw, 0.625rem) via --type-size-050 |
--form-height-lg | component | 48px |
--form-height-md | component | 40px |
--form-height-sm | component | 32px |
--form-height-xs | component | 28px |
--form-help-color | component | #838383 via --color-text-muted → --color-gray-10 |
--form-help-gap | component | 0.25rem via --spacing-100 |
--form-label-color | component | #646464 via --color-text-secondary → --color-gray-11 |
--form-label-font-size | component | var(--_field-font-size) |
--form-label-font-weight | component | 500 via --font-weight-medium |
--form-label-gap | component | 0.25rem via --spacing-100 |
--form-padding-x-lg | component | 1rem via --spacing-400 |
--form-padding-x-md | component | 0.75rem via --spacing-300 |
--form-padding-x-sm | component | 0.625rem via --spacing-250 |
--form-padding-x-xs | component | 0.5rem via --spacing-200 |
--form-padding-y-lg | component | 0.75rem via --spacing-300 |
--form-padding-y-md | component | 0.5rem via --spacing-200 |
--form-padding-y-sm | component | 0.375rem via --spacing-150 |
--form-padding-y-xs | component | 0.25rem via --spacing-100 |
--form-placeholder-color | component | #838383 via --color-text-muted → --color-gray-10 |
--form-radius-lg | component | 0.5rem via --radius-200 |
--form-radius-md | component | 0.5rem via --radius-200 |
--form-radius-sm | component | 0.25rem via --radius-100 |
--form-radius-xs | component | 0.25rem via --radius-100 |
--form-text-color | component | #202020 via --color-text-primary → --color-gray-12 |
--form-affix-bg | #f0f0f0 via --color-surface-sunken → --color-gray-3 | |
--form-affix-border-color | #cecece via --form-border-color → --color-border → --color-gray-7 | |
--form-affix-color | #646464 via --color-text-secondary → --color-gray-11 | |
--color-danger-strong | semantic | #ce2c31 via --color-red-11 |
--font-sans | semantic | 'DM Sans', sans-serif |
--transition-fast | semantic | 150ms ease |
--type-size-050 | semantic | clamp(0.5rem, 0.44rem + 0.3vw, 0.625rem) |
--type-size-100 | semantic | clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) |
--type-size-150 | semantic | clamp(0.6875rem, 0.61rem + 0.38vw, 0.875rem) |
--type-size-200 | semantic | clamp(0.75rem, 0.66rem + 0.44vw, 0.9375rem) |
--type-size-300 | semantic | clamp(0.875rem, 0.77rem + 0.52vw, 1.125rem) |