Skip to content

Text field

Text fields are hairline boxes with an uppercase label above. On focus the box turns accent and doubles to 2px; on an error it turns red and says what to fix.

CSS
css/components/field.css, css/components/input.css
JS
js/components/form.js

01 — Variants

Text input

A label, the input, and an optional hint. .field__req marks a required field; .field__optional marks an optional one.

Shown on your profile.

With a counter

Add maxlength and data-counter and the field counts down as people type.

A few words.

Text area

Grows with its content as people type.

Select

The native select, with a hairline chevron. Best for picking one of a few known values in a form.

02 — States

Filled, error, disabled and suggested

Click into a field to see focus. An error names the problem and how to fix it. A suggested value (for example, alt text drafted by an assistant) is marked until someone edits it.

Add a title before posting.

03 — Usage

Use it for

  • A visible label on every field. Placeholders are examples, not labels.
  • Hints for format and limits, before people make a mistake.
  • Error messages that say what to do: “Add a title before posting”.

Avoid

  • Placeholder text as the only label.
  • Disabling a field without saying why.
  • A select with only two options. Use radio buttons.

04 — Accessibility

Notes

  • Every input has a <label for>. Hints, counters and errors are linked with aria-describedby.
  • Errors set aria-invalid="true"; form.js moves focus to the first error on submit.
  • Field edges are 40% white (3.25:1), above the 3:1 that input edges need. Fields are 44px tall.

05 — Without Jewel’s JS

What the script does

form.js validates fields, shows errors, counts characters, grows text areas where CSS can’t, and sets the busy state on submit. The CSS reads only the markup below, so a framework can set it itself and leave out form.js and data-component.

StateSet thisNotes
Erroraria-invalid="true" on the control; the message in .field__error without hidden, and its id in aria-describedbyRed box and red message. Undo all three when it’s fixed.
Counter“12 / 60” in .field__counteris-over turns it red past the limit.
Suggesteddata-suggested on the .input; the label’s .field__badge without hiddenMuted italic until someone edits it. Then remove both.
Busyaria-busy="true" and disabled on the submit buttonShows a spinner. The script also sets aria-busy on the form.
Form messagesText in .form__error (role="alert") or .form__status (role="status")Each hides while it’s empty.
Text area heightNothing, where field-sizing: content worksElsewhere the script sets the height from scrollHeight as people type.

Events and keyboard

  • Set noValidate on the form so the browser’s own error bubbles don’t show.
  • The script checks a field when it loses focus after its first edit, rechecks an invalid field as people type, and checks every field on submit. Then it focuses the first error and scrolls it into view.
  • jewel:submit doesn’t fire without the script.