Primitive · 3 of 13
A stacked-label field: the label is absolutely positioned over the input, and the input reserves room for it with top padding. Focus is a halo, never an outline.
| Prop | Type | Notes |
|---|---|---|
| id * | string | Wires label[for] to input[id]. |
| label * | string | Rendered over the field, always visible. |
| type * | 'text' | 'email' | No tel, url or password. |
| placeholder * | string | Renders at 12px in --color-body-secondary. |
| autocomplete | string | Passed straight through. |
| isRequired | boolean | Adds the asterisk icon and the native required attribute. |
| isError | boolean | Swaps text and halo to the error ramp. |
| errorMessage | string | Only rendered when isError. |
| modelValue | string | v-model via update:modelValue on input. |
The real InputText.vue, compiled and mounted. Click into the
fields — hover, focus, v-model and the error halo are all live.
The error message is not announced.
It renders in a plain <span> with no role="alert",
no aria-live, and no aria-describedby linking it to
the input. The input also never gets aria-invalid. A screen
reader user gets no signal that the field failed — the error is conveyed by
color and position only.
Error text is #B42318 at 12px — 3.01:1.
Below the 4.5:1 floor for body text, and error copy is exactly the text a
user most needs to read. The system carries error-100
#FE9A9A, which clears it comfortably (9.71:1) on the #0A0A0B
page. Recorded in DESIGN.md §10.2.
Placeholder and label can collide.
The label sits at pl-3 pt-3 and the input reserves
pt-7, so the two clear each other at the default 14px label. A
longer label that wraps to a second line will overlap the value, because the
reserved padding is a fixed 1.75rem rather than measured.
Autofill is handled deliberately.
Four :-webkit-autofill rules force a 1000px inset shadow in
--color-surface-tertiary and re-tint the text to
--color-heading, defeating Chrome's default light-yellow fill.
Without this the field would flash white on a dark form.