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.
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 witharia-describedby. - Errors set
aria-invalid="true";form.jsmoves 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.
| State | Set this | Notes |
|---|---|---|
| Error | aria-invalid="true" on the control; the message in .field__error without hidden, and its id in aria-describedby | Red box and red message. Undo all three when it’s fixed. |
| Counter | “12 / 60” in .field__counter | is-over turns it red past the limit. |
| Suggested | data-suggested on the .input; the label’s .field__badge without hidden | Muted italic until someone edits it. Then remove both. |
| Busy | aria-busy="true" and disabled on the submit button | Shows a spinner. The script also sets aria-busy on the form. |
| Form messages | Text in .form__error (role="alert") or .form__status (role="status") | Each hides while it’s empty. |
| Text area height | Nothing, where field-sizing: content works | Elsewhere the script sets the height from scrollHeight as people type. |
Events and keyboard
- Set
noValidateon 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:submitdoesn’t fire without the script.