Components · Forms

Text field

Where a clinician types something that will end up in a patient's record. Its boundary must be findable, its label must be permanent, and its errors must never destroy what was entered.

Stable · v1.0 shadcn/ui WCAG 2.2 AA SC 1.4.11

Overview

Text input is where the most ordinary-looking accessibility failures do the most clinical damage. A placeholder used as a label vanishes the moment someone types. A 1.1:1 border makes the field itself hard to locate. An error that clears the input costs a clinician the measurement they just read off a monitor.

None of those are edge cases — they are the default behaviour of most component libraries, including stock shadcn's border treatment. This component fixes them and the token gate keeps them fixed.

Findable

The boundary of the control is visible without hovering or guessing, at 3:1 against every surface it can sit on.

Permanently labelled

The label is outside the field and stays there. Nothing that identifies an input disappears when the input is used.

Never destructive

Validation explains; it does not clear, reformat or reject silently. What the clinician typed survives until they change it.

Anatomy

Text field · default, hint, error
ng/L. Leave blank if not yet resulted.
MRN must be 9 digits — 7 entered. Check the wristband and re-enter.
ElementRule
Label A real <label for>, above the field, always visible. Never a placeholder.
Input 44 px tall — the touch floor — with a --input border at ≥ 3:1. 16 px text minimum, which also prevents iOS zoom-on-focus.
Hint Units, format, or what "blank" means. Wired with aria-describedby so it is announced, not just seen.
Error Replaces the hint, states what is wrong and what to do. Two-pixel border plus text — never colour alone.
The border is a governed token

Stock shadcn sets --input equal to --border, which in this palette measured 1.10:1 against the surfaces it sits on — far below the 3:1 that WCAG 2.2 SC 1.4.11 requires for identifying a control. --input is therefore a governed token, gated in CI against background, card and muted surfaces. A divider may be faint; a control boundary may not.

Labels, not placeholders

Do
ng/L

Label and unit both survive typing. A colleague reading over a shoulder knows what 1240 is.

Don't

The placeholder that said "Peak troponin (ng/L)" is gone. The number is now unitless and unlabelled in a clinical record.

Placeholders may be used for examples of format — e.g. 1964-03-11 — never for the name of the field, and never as the only statement of a required unit.

Validation

Out-of-range: confirm, do not reject
ng/L
25 000 ng/L is outside the usual range
Typical peak in confirmed STEMI is 1 000–10 000 ng/L. Confirm this is correct, or re-check the result.

Clinical input rules

KindRule
Measurements Unit shown as a permanent label or suffix, never assumed. Value and unit are stored together.
Identifiers Mono stack, no auto-capitalisation, no autocorrect, no autocomplete from a browser profile. Grouping is display-only; the stored value is unformatted.
Dates ISO 8601 with a visible format hint. Never a locale-ambiguous mask.
Free-text clinical notes Never truncated silently. If a limit exists it is shown from the start with a live remaining count.
Passwords Paste always permitted. A reveal toggle is provided. Never impose composition rules that force written-down credentials.
No autocomplete on identifiers

Browser autofill on an MRN or patient-name field can suggest a different patient the clinician entered earlier that shift. Patient identifier inputs set autocomplete="off", autocorrect="off" and spellcheck="false". Credential fields do the opposite — they carry correct autocomplete tokens so password managers work.

States

All states
MRN must be 9 digits — 6 entered.
Available once the ECG has been analysed.

Read-only and disabled are different. Read-only is a value the clinician may read and copy but not change — model version, acquisition time. Disabled is an input that is not currently applicable, and it always carries adjacent text saying what would make it applicable. A disabled field with no explanation is a dead end.

Do's and don'ts

Do
9 digits, as printed on the wristband

Autofill and autocorrect off, mono stack so 1 and l cannot be confused.

Don't
Suggested from earlier this shift

Browser autofill on an MRN field, offering a different patient the clinician saw an hour ago.

Do
ng/L

type="text" with inputmode. A scroll gesture over the field scrolls the page.

Don't
ng/L

type="number". A scroll over the focused field silently changes the recorded value.

Accessibility

Outcomes of use

What this contributes to, in the terms of Usability & context of use. These are attributes believed to contribute to an outcome; the outcome itself is settled by observing real use in a specified context, not by this page.

Clinical safety notes

Risk controls carried by this component

Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1).

Implementation

$ npx shadcn@latest add https://md.notjustany.tech/r/text-field.json
// Label, hint and error wiring is done by the component — the ids and
// aria-describedby cannot be forgotten at the call site.
<TextField
  label="Peak troponin"
  hint="ng/L. Leave blank if not yet resulted."
  inputMode="numeric"
  value={value}
  onChange={setValue}
  onBlur={validate}
  error={error}                 // sets aria-invalid + replaces hint
/>

// Patient identifier — autofill and correction disabled.
<TextField
  label="Patient identifier"
  variant="identifier"   // mono, autocomplete/correct/spellcheck off
  hint="9 digits, as printed on the wristband"
/>
PropTypeDefaultNotes
labelstring Required. Rendering without one fails lint.
varianttext | identifier | measurement | secret text Sets the font stack and the autocomplete / correction policy.
unitstring Required for measurement; rendered as a permanent suffix.
hintstring Wired via aria-describedby.
errorstring Sets aria-invalid. Never clears the value.
readOnlybooleanfalse Value is selectable and copyable.
disabledbooleanfalse Requires hint explaining the precondition.
NotJustAnyMed.Tech Design System · Text field · v1.0 · draft for review
Reference applications named in this system are fictional; all patient data shown is fabricated. Reference ranges are illustrative, not clinical guidance.