j3w1/theme implementation brief — text-field
theme: j3w1-theme 1.1.0 profile: default sourceDigest: sha256-C+iD5Q7l0XPU/boMj003wPbPLBOq3C3ZM7s4OfzJ6mc=
pin: https://raw.githubusercontent.com/j3w1/theme/v1.1.0/exports/components/text-field.json
spec: spec/components/text-field.md site: https://j3w1.github.io/theme/reference/#c-text-field
maturity: stable priority: R1
Summary: Single-line free-text entry, covering every native input type that renders as a line box; search, number and password are variants, not separate components.
Use the pinned approved default profile. Pending roles in that profile use-and-report their decision IDs; this does not approve them. Proposed profiles are preview-only and blocked for delivery. Heritage profiles are historical-only. Deprecated or heritage roles are blocked for new approved-profile mappings. Consume roles within their documented scope, never primitives. Release numbering does not approve profiles or tokens.
Tokens (resolved):
root.bg #000000 ({color.surface.input}) use
root.border #a3676b ({color.border.control}) use-and-report (D-007)
root.bg-hover #1c0a09 ({color.interaction.hover.bg}) use
root.border-focus #e53935 ({color.border.active}) use
root.ring #e53935 ({color.interaction.focus.ring}) use
root.ring-invalid #ffa2a7 ({color.interaction.focus.ring-container}) use
root.border-invalid #e53935 ({color.status.danger.border}) use
root.border-disabled #7d1310 ({color.border.disabled}) use
root.bg-disabled #160b0b ({color.interaction.disabled.bg}) use
root.border-readonly #2b0e0d ({color.border.divider}) use-and-report (D-008)
input.text #e99499 ({color.text.default}) use
input.text-disabled #8a5559 ({color.text.disabled}) use
input.placeholder #a3676b ({color.text.placeholder}) use
input.caret #e99499 ({color.code.caret}) use
input.selection-bg #911410 ({color.interaction.text-selection.bg}) use
input.selection-text #f4eeee ({color.interaction.text-selection.text}) use
label.text #ffa2a7 ({color.text.bright}) use
label.required-mark #f73f35 ({color.status.danger.text}) use
help.text #bd787d ({color.text.muted}) use
message.text #f73f35 ({color.status.danger.text}) use
affix.text #bd787d ({color.text.muted}) use
affix.border #2b0e0d ({color.border.divider}) use-and-report (D-008)
action.text #e99499 ({color.action.tertiary.text}) use
action.bg-hover #1c0a09 ({color.action.tertiary.hover-bg}) use
loading.glyph #bd787d ({color.text.muted}) use
State dependency disclosures: D-007
States: default, hover, focus-visible, placeholder-shown, filled, required, invalid, invalid+focus-visible, invalid+hover, disabled, disabled+filled, read-only, read-only+focus-visible, loading
default: 1px {color.border.control} on {color.surface.input}; text {color.text.default} [—]
hover: background → {color.interaction.hover.bg}; border unchanged [cursor: text]
focus-visible: border → {color.border.active}; ring 1px dashed {color.interaction.focus.ring} at −2px [the ring]
placeholder-shown: placeholder in {color.text.placeholder}, italic [italic]
filled: as default with a value [—]
required: `*` after the label in {color.status.danger.text} plus visually hidden "required" [`required` attribute; the mark]
invalid: border 2px {color.status.danger.border}; message with the ✕ glyph [border width 1 → 2px; glyph; `aria-invalid`]
invalid+focus-visible: the 2px danger border and a 1px dashed ring in {color.interaction.focus.ring-container} at −4px (the control form, recoloured; never the 2px container form) [double boundary]
invalid+hover: as invalid with the hover background [cursor]
disabled: text {color.text.disabled}; border {color.border.disabled}; background {color.interaction.disabled.bg}; no hover; the label and help keep their colours [`disabled`; cursor: not-allowed]
disabled+filled: as disabled with a value [`disabled`]
read-only: no box: 1px dotted {color.border.divider} bottom edge on {color.surface.canvas}; text {color.text.default}; focusable and selectable [`readonly`; dotted edge]
read-only+focus-visible: the dotted {color.border.divider} edge unchanged, plus the dashed ring at −2px [the ring]
loading: size unchanged; a static `⋯` glyph in {color.text.muted} inside the box after the input and before any trailing action; input remains editable [glyph; `aria-busy`]
Keyboard:
Tab / Shift+Tab: Moves focus in and out; affix actions (clear, reveal, stepper) are separate tab stops after the input.
Escape: Clears the value in the search variant when it is non-empty; otherwise nothing.
Enter: Submits the owning form (native).
Up / Down: Number variant steps by the step attribute; Shift multiplies by ten.
ARIA: native with