j3w1 / Text fieldVue · HTML · React · Astro

J3W1 / DESIGN SYSTEM

Text field

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.

stable · LIGHT DOM · 1.1.0

Visible native controls retain FormData, labels, constraints, disabled fieldsets and reset. No duplicate form-value mechanism is installed.

Live variants

default

Shown on your profile and in the window title.

✕ Use 2 to 32 characters.

<j3w1-text-field id="example-text-field-default"><div class="text-field">
  <label class="text-field-label" for="example-text-field-default-tf-name">Display name <span class="text-field-required" aria-hidden="true">*</span><span class="text-field-required sr-only"> required</span></label>
  <div class="text-field-root">
    <input class="text-field-input" id="example-text-field-default-tf-name" type="text" value="j3w1" placeholder="Your name" aria-describedby="example-text-field-default-tf-name-help example-text-field-default-tf-name-message" name="example-text-field-default-tf-name">
    <span class="text-field-loading" aria-hidden="true">⋯</span>
  </div>
  <p class="text-field-help" id="example-text-field-default-tf-name-help">Shown on your profile and in the window title.</p>
  <p class="text-field-message" id="example-text-field-default-tf-name-message">✕ Use 2 to 32 characters.</p>
</div></j3w1-text-field>

password

Twelve characters or more; spaces allowed.

✕ Too short.

<j3w1-text-field id="example-text-field-password"><div class="text-field">
  <label class="text-field-label" for="example-text-field-password-tf-pass">Passphrase <span class="text-field-required" aria-hidden="true">*</span><span class="text-field-required sr-only"> required</span></label>
  <div class="text-field-root">
    <input class="text-field-input" id="example-text-field-password-tf-pass" type="password" required="" value="correct horse" placeholder="At least 12 characters" aria-describedby="example-text-field-password-tf-pass-help example-text-field-password-tf-pass-message" name="example-text-field-password-tf-pass">
    <button class="text-field-action" type="button" aria-pressed="false" aria-label="Reveal passphrase">reveal</button>
    <span class="text-field-loading" aria-hidden="true">⋯</span>
  </div>
  <p class="text-field-help" id="example-text-field-password-tf-pass-help">Twelve characters or more; spaces allowed.</p>
  <p class="text-field-message" id="example-text-field-password-tf-pass-message">✕ Too short.</p>
</div></j3w1-text-field>

search

Escape clears the query.

✕ No such path.

<j3w1-text-field id="example-text-field-search"><div class="text-field">
  <label class="text-field-label" for="example-text-field-search-tf-search">Search files <span class="text-field-required" aria-hidden="true">*</span><span class="text-field-required sr-only"> required</span></label>
  <div class="text-field-root">
    <span class="text-field-affix" aria-hidden="true">/</span>
    <input class="text-field-input" id="example-text-field-search-tf-search" type="search" value="site.css" placeholder="Name or path" aria-describedby="example-text-field-search-tf-search-help example-text-field-search-tf-search-message">
    <button class="text-field-action" type="button" aria-label="Clear search">clear</button>
    <span class="text-field-loading" aria-hidden="true">⋯</span>
  </div>
  <p class="text-field-help" id="example-text-field-search-tf-search-help">Escape clears the query.</p>
  <p class="text-field-message" id="example-text-field-search-tf-search-message">✕ No such path.</p>
</div></j3w1-text-field>

number

Up and Down step by one; Shift steps by ten.

✕ Between 0 and 64.

<j3w1-text-field id="example-text-field-number"><div class="text-field">
  <label class="text-field-label" for="example-text-field-number-tf-gap">Inner gap <span class="text-field-required" aria-hidden="true">*</span><span class="text-field-required sr-only"> required</span></label>
  <div class="text-field-root">
    <input class="text-field-input text-field-input-numeric" id="example-text-field-number-tf-gap" type="number" value="14" min="0" max="64" step="1" placeholder="0" aria-describedby="example-text-field-number-tf-gap-help example-text-field-number-tf-gap-message" name="example-text-field-number-tf-gap">
    <span class="text-field-affix" aria-hidden="true">px</span>
    <span class="text-field-stepper">
      <button class="text-field-action" type="button" aria-label="Increase inner gap">+</button>
      <button class="text-field-action" type="button" aria-label="Decrease inner gap">−</button>
    </span>
    <span class="text-field-loading" aria-hidden="true">⋯</span>
  </div>
  <p class="text-field-help" id="example-text-field-number-tf-gap-help">Up and Down step by one; Shift steps by ten.</p>
  <p class="text-field-message" id="example-text-field-number-tf-gap-message">✕ Between 0 and 64.</p>
</div></j3w1-text-field>

affix

Host only; the path is fixed.

✕ Not a valid host.

<j3w1-text-field id="example-text-field-affix"><div class="text-field">
  <label class="text-field-label" for="example-text-field-affix-tf-site">Site <span class="text-field-required" aria-hidden="true">*</span><span class="text-field-required sr-only"> required</span></label>
  <div class="text-field-root">
    <span class="text-field-affix" aria-hidden="true">https://</span>
    <input class="text-field-input" id="example-text-field-affix-tf-site" type="text" value="j3w1.github.io" placeholder="host" aria-describedby="example-text-field-affix-tf-site-help example-text-field-affix-tf-site-message" name="example-text-field-affix-tf-site">
    <span class="text-field-affix" aria-hidden="true">/theme/</span>
    <span class="text-field-loading" aria-hidden="true">⋯</span>
  </div>
  <p class="text-field-help" id="example-text-field-affix-tf-site-help">Host only; the path is fixed.</p>
  <p class="text-field-message" id="example-text-field-affix-tf-site-message">✕ Not a valid host.</p>
</div></j3w1-text-field>

Install and register

Use the exact version. If npm publication is pending, install the release tarball from the installation guide.

npm install --save-exact @j3w1/ui@1.1.0
import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/text-field.css';
import '@j3w1/ui/register/text-field';

Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.

Vue SFC, React, Astro and plain HTML integration →

Copy the complete implementation

npx --package=@j3w1/ui@1.1.0 j3w1-ui copy text-field --out ./vendor/j3w1/text-field

The CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.

Public API

attributes

NameType / signatureMeaning
disabledCustomEventDisable the native controls owned by this instance.
loadingCustomEventMark the primary control busy and prevent duplicate activation.
valueCustomEventForwards value to the primary native control.
requiredCustomEventForwards required to the primary native control.
readonlyCustomEventForwards readonly to the primary native control.
checkedCustomEventForwards checked to the primary native control.

properties

NameType / signatureMeaning
disabledbooleanDelegates disabled state to native children.
namestringNative control name; application form keys remain explicit.
valuestringPrimary native value; assignment does not fabricate user events.
checkedbooleanNative checkbox state.
requiredbooleanNative required constraint.
readOnlybooleanNative readonly property.
formHTMLFormElement | nullOwning native form. Read only.

methods

NameType / signatureMeaning
refresh(): voidReconnect behavior after replacing native child structure.
focus(options?: FocusOptions): voidFocus the first usable native control.
checkValidity(): booleanCheck owned native constraints.
reportValidity(): booleanReport the first invalid owned control.
clear(): voidClear an editable primary input and emit native input/change.
stepBy(amount: number): voidStep a number input by the supplied number of native steps.

events

NameType / signatureMeaning
j3w1-changeCustomEventNative change from an owned control, alongside the original native event.

Dependencies and verification

Registered components: none. Style dependencies: none.

Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.

Full implementation manifest, API and token dependency closure

Canonical specification

Purpose

Single-line free-text entry. Every native type that renders as a line box (text, password, search, number, email, url, tel) is this component; date and time inputs are the date-picker and time-picker components because their popups are host surfaces.

Anatomy

Label above the control; the control box; optional prefix and suffix inside the box separated by a color.border.divider rule; the input; optional trailing actions (clear, reveal, stepper); help text below; the validation message, which takes the help slot visually while help stays in the DOM and aria-describedby lists both.

States

StateVisualNon-colour channel
default1px color.border.control on color.surface.input; text color.text.default
hoverbackground → color.interaction.hover.bg; border unchangedcursor: text
focus-visibleborder → color.border.active; ring 1px dashed color.interaction.focus.ring at −2pxthe ring
placeholder-shownplaceholder in color.text.placeholder, italicitalic
filledas default with a value
required* after the label in color.status.danger.text plus visually hidden “required”required attribute; the mark
invalidborder 2px color.status.danger.border; message with the ✕ glyphborder width 1 → 2px; glyph; aria-invalid
invalid+focus-visiblethe 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+hoveras invalid with the hover backgroundcursor
disabledtext color.text.disabled; border color.border.disabled; background color.interaction.disabled.bg; no hover; the label and help keep their coloursdisabled; cursor: not-allowed
disabled+filledas disabled with a valuedisabled
read-onlyno box: 1px dotted color.border.divider bottom edge on color.surface.canvas; text color.text.default; focusable and selectablereadonly; dotted edge
read-only+focus-visiblethe dotted color.border.divider edge unchanged, plus the dashed ring at −2pxthe ring
loadingsize unchanged; a static glyph in color.text.muted inside the box after the input and before any trailing action; input remains editableglyph; aria-busy

Precedence when several apply: disabled > loading > invalid > hover; focus-visible is always drawn.

Keyboard

Focus order is label (not focusable) → input → clear or reveal → stepper buttons. The clear button is type="button" with aria-label="Clear <label>"; the reveal button toggles type between password and text and its own aria-pressed. Escape in the search variant clears a non-empty value.

Accessibility

A programmatic label is required (<label for>). Help and message are linked through aria-describedby in that order. aria-invalid="true" is set only after the user interacted with the field or submitted the form, never on first paint. The message is announced on submit through the form’s live region, not on every keystroke. Contrast: text 8.65:1 on the input surface, placeholder 5.81:1, disabled text 3.33:1 (exempt), control border 4.45:1 (≥ 3:1), invalid border 4.69:1, danger message 5.40:1. The box is at least 24×24 CSS pixels in compact density.

Portability

Everything is expressible with border, background and outline; the only pseudo-element is the required mark. Toolkits without dashed outlines draw a solid 1px ring in the same colour and record the deviation. Toolkits without a read-only style treat read-only as default text and record it.

Non-examples

Rounded corners. A floating or animated label. An underline-only field with no box. A glow instead of the dashed ring. Placeholder text in red. Disabling by opacity. Using colour alone for the invalid state. A hover that changes the text colour.

j3w1 / command

  • SPEC Overview
  • SPEC Foundations
  • SPEC Components
  • SPEC Patterns
  • SPEC Tokens
  • SPEC Tools
  • SPEC Implement
  • SPEC For agents
  • SPEC Ports
  • SPEC Releases
  • DEMO Vue application demo
  • SPEC Complete UI theme specification