Skip to main content

Core · Form fields

Inputs

One control for each type of input: the type tells the browser the keyboard, the picker and the check.

Every control is a native element: the browser owns the state, the keyboard, the validation and the submission, with or without JavaScript. LUZ does not mark a field invalid: add .luz-input--error, aria-invalid="true" and the error text. A field wraps a control in .luz-field:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="name">Project name</label>
  <input class="luz-input" id="name" type="text" aria-describedby="name-hint name-error">
  <div class="luz-field__helper" id="name-hint">Shown on the dashboard.</div>
  <div class="luz-field__error" id="name-error"><span class="u-sr-only">Error: </span>Use letters, numbers and dashes only.</div>
</div>

Text

A single-line field for a short answer you cannot list in advance, such as a name.

Shown on the dashboard.
Error: Use letters, numbers and dashes only.

Code

Copy the markup

A label and an input need no class. .luz-field adds a hint and an error:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="name">Project name</label>
  <input class="luz-input" id="name" type="text" aria-describedby="name-hint">
  <div class="luz-field__helper" id="name-hint">Shown on the dashboard.</div>
</div>

Variants and options

  • required, with .luz-field__label--required for the red asterisk.
  • Error: .luz-input--error, aria-invalid="true" and a .luz-field__error after the field.
  • disabled: faded, skipped by Tab, not sent.
  • readonly: dashed underline, reachable, sent.
  • list with a <datalist>: suggestions the reader may ignore.

How it works

A native <input> styled by components/form.css. LUZ does not mark a field invalid for you: add the error class, aria-invalid and the message when your validation fails.

Without JavaScript

The field works and submits with its form.

Keyboard and ARIA

  • Tab moves focus into the field, Shift+Tab out of it.
  • The label is the name; the hint and the error are read after it. A placeholder is not a name.

Contrast and focus are in the Design tab.

Design

When to use

Use a text field for a short answer you cannot list in advance.

  • A name, a title, a code, a reference.
  • An address, a number or a date: use the field of that type, such as email, number or date.

When not to use

Do not make the reader type what they could pick.

  • A choice from a list: a select or radio buttons.
  • A password: its field hides what is typed. Several lines: a textarea.

Do and don't

Do A visible label: it stays on screen while the reader types.
Don't A placeholder as the label: it disappears when the reader types.

Writing the content

  • The label is a short noun in sentence case: "Project name".
  • The hint says the format or why you ask.
  • The error says what is wrong and how to fix it.

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Email

A field for an email address: the browser checks its shape and phones show the at sign.

We send your receipt here.

Code

Copy the markup

A text field with type="email"; autocomplete="email" lets the browser fill it in.

HTML
<div class="luz-field">
  <label class="luz-field__label" for="email">Email</label>
  <input class="luz-input" id="email" type="email" autocomplete="email" required aria-describedby="email-hint">
  <div class="luz-field__helper" id="email-hint">We send your receipt here.</div>
</div>

Variants and options

  • multiple: several addresses, separated by commas.
  • The classes and states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="email">. The browser checks the shape when the form is sent, not that the address exists.

Without JavaScript

The browser checks the address when the form is sent, with no script.

Keyboard and ARIA

  • Tab moves focus in, Shift+Tab out. On a phone the keyboard shows the at sign.
  • The label is the name; the hint and the error are read after it.

Contrast and focus are in the Design tab.

Design

When to use

Use an email field whenever the answer is an email address.

  • Signing up, signing in, contact forms and receipts.
  • Say in the hint what the address is for.

When not to use

An email field checks the shape of an address, not that it works.

  • Do not use a text field for an address: the reader loses the check and the keyboard.
  • Do not rely on it to prove the address is real: send a message and ask for confirmation.

Do and don't

Do An error that says how to fix the address.
Don't An error that only says something is wrong.

Writing the content

  • Label it "Email", not "Enter your email address here".
  • The hint says what the address is used for.
  • The error says what to change: an address has an @ and a domain.

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Password

A field that hides what is typed, for a password or another secret.

At least 8 characters.

Code

Copy the markup

A text field with type="password". Set autocomplete to say what the field is:

HTML
<div class="luz-field">
  <label class="luz-field__label luz-field__label--required" for="password">Password</label>
  <input class="luz-input" id="password" type="password" autocomplete="current-password" minlength="8" required aria-describedby="password-hint">
  <div class="luz-field__helper" id="password-hint">At least 8 characters.</div>
</div>

Variants and options

  • autocomplete="current-password" to sign in; "new-password" to sign up or change it.
  • minlength, maxlength, pattern: the browser checks them when the form is sent.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="password"> styled like any text field. The browser draws dots instead of the characters. LUZ adds no show-password button: one you add is your own script.

Without JavaScript

The field works and submits with its form.

Keyboard and ARIA

  • Tab moves focus in, Shift+Tab out.
  • The label is the name; the hint says the rules before the reader types. Do not block paste: readers paste long passwords.

Contrast and focus are in the Design tab.

Design

When to use

Use a password field for any secret the reader types and that must not show on screen.

  • Signing in, signing up and changing a password.
  • Set autocomplete to current-password or new-password.

When not to use

A password field is for secrets, not for every code.

  • A code sent by text message or email: a text field with inputmode="numeric" and autocomplete="one-time-code".
  • Something the reader must see to check it, such as an address: a text field.

Do and don't

Do Say what the field is: current-password to sign in, new-password to sign up.
Don't A text field for a secret: it shows what is typed to anyone looking at the screen.

Writing the content

  • Label it "Password", not "Enter your password".
  • The hint states the rules before the reader types: "At least 8 characters."
  • The error says what is missing: "Use at least 8 characters."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

A text field for words to look for, with a search key on phone keyboards and a clear button in some browsers.

Search by name or topic.

Code

Copy the markup

A text field with type="search" inside a form with role="search", so the page has a search landmark:

HTML
<form role="search">
  <div class="luz-field">
    <label class="luz-field__label" for="search">Search the docs</label>
    <input class="luz-input" id="search" type="search" name="q" enterkeyhint="search" aria-describedby="search-hint">
    <div class="luz-field__helper" id="search-hint">Search by name or topic.</div>
  </div>
  <button type="submit">Search</button>
</form>

Variants and options

  • enterkeyhint="search": the Enter key of a phone keyboard says Search.
  • list with a <datalist>: suggestions the reader may ignore.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="search"> styled like any text field. Some browsers add a button inside it that clears it; that button is the browser's and LUZ does not draw it.

Without JavaScript

Enter submits the form, as a request to your search address. No script is needed.

Keyboard and ARIA

  • Tab moves focus in. Enter sends the search.
  • The label is the name; the hint and the error are read after it. A placeholder is not a name.

Contrast and focus are in the Design tab.

Design

When to use

Use a search field to find something by words, in a list, a page or a site.

  • A filter above a table or a list.
  • For the whole site, use search, which has a dialog and an index.

When not to use

Do not use a search field for an answer that is a name or a title.

  • A name, a title or a reference: a text field.
  • A choice the reader can list: a select or radio buttons.

Do and don't

Do A visible label that says what is searched.
Don't A placeholder as the only name: it disappears when the reader types.

Writing the content

  • The label says what is searched: "Search the docs".
  • The button is a verb: "Search".
  • With no results, say so and offer a way out: "No results for “forms”. Try fewer words."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Telephone

A field for a phone number: phones show a number keypad, and the browser checks no format.

With the country code, such as +34 600 000 000.

Code

Copy the markup

A text field with type="tel"; autocomplete="tel" lets the browser fill it in:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="tel">Phone</label>
  <input class="luz-input" id="tel" type="tel" autocomplete="tel" aria-describedby="tel-hint">
  <div class="luz-field__helper" id="tel-hint">With the country code, such as +34 600 000 000.</div>
</div>

Variants and options

  • autocomplete="tel": the browser fills in the reader's number.
  • pattern: only when you know the format, with an error that says it; the browser checks nothing else.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="tel">. Phone numbers differ by country, so the browser only chooses the keypad and checks no format. Check the number on the server.

Without JavaScript

The field works and submits its text with the form.

Keyboard and ARIA

  • Tab moves focus in, Shift+Tab out. On a phone the keyboard is a number keypad.
  • The label is the name; the hint says the format you accept, and the error says what to change.

Contrast and focus are in the Design tab.

Design

When to use

Use a telephone field whenever the answer is a phone number.

  • Contact, delivery and booking forms.
  • Say in the hint whether the country code is needed, or accept any format and tidy it on the server.

When not to use

A telephone field checks nothing, and it is not for every number.

  • A quantity the reader adds up: a number field.
  • A code or reference made of digits: a text field with inputmode="numeric".

Do and don't

Do A hint that shows the format you accept.
Don't A pattern of ten digits: a number written with a plus or spaces is refused.

Writing the content

  • Label it "Phone", not "Phone number (digits only)".
  • The hint gives one example of the format.
  • The error says what to change: "Enter a phone number with the country code."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

URL

A field for a web address: the browser checks for a scheme such as https:// and phones show a slash key.

Start with https://.

Code

Copy the markup

A text field with type="url"; autocomplete="url" lets the browser fill it in:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="url">Website</label>
  <input class="luz-input" id="url" type="url" autocomplete="url" placeholder="https://example.com" aria-describedby="url-hint">
  <div class="luz-field__helper" id="url-hint">Start with https://.</div>
</div>

Variants and options

  • pattern="https://.*": only secure addresses.
  • list with a <datalist>: suggestions the reader may ignore.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="url">. When the form is sent the browser checks that the value is a full address with a scheme: https://example.com passes, example.com does not. It accepts any scheme, so before you turn the value into a link, check on the server that it is http or https.

Without JavaScript

The browser checks the address when the form is sent, with no script.

Keyboard and ARIA

  • Tab moves focus in, Shift+Tab out. On a phone the keyboard shows the slash and the dot.
  • The label is the name; the hint says the address starts with https://.

Contrast and focus are in the Design tab.

Design

When to use

Use a URL field when the answer is the address of a page or a site.

  • A website, a profile or a portfolio.
  • Say in the hint that the address starts with https://: the browser refuses one without a scheme.

When not to use

A URL field takes only a full address, and it proves nothing about the site.

  • A path such as /docs/start: a text field.
  • Do not trust the value: the browser does not check that the site exists or is safe.

Do and don't

Do Say in the hint that the address starts with https://.
Don't A URL field for a path: the browser refuses it.

Writing the content

  • Label it "Website", not "Enter the URL of your website".
  • The hint says how it starts: "Start with https://."
  • The error says what to change: "Enter a full address, starting with https://."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Number

A field for a quantity counted or adjusted in steps, with spin buttons and a range check.

From 1 to 10.

Code

Copy the markup

A text field with type="number"; min, max and step set what is valid:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="number">Quantity</label>
  <input class="luz-input" id="number" type="number" min="1" max="10" step="1" value="1" aria-describedby="number-hint">
  <div class="luz-field__helper" id="number-hint">From 1 to 10.</div>
</div>

Variants and options

  • min, max: checked when the form is sent, and the limits of the spin buttons.
  • step: the size of a step; step="any" allows decimals.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="number">. The browser adds spin buttons, sends the value written with a dot, and checks min, max and step when the form is sent. The spin buttons are the browser's: LUZ does not style them.

Without JavaScript

The browser checks the number when the form is sent, with no script.

Keyboard and ARIA

  • Tab moves focus in. Up and Down arrows add or take away one step.
  • The label is the name; say the range in the hint.

Contrast and focus are in the Design tab.

Design

When to use

Use a number field for a quantity: something the reader counts, adds or takes away.

  • Items in a basket, guests, seats.
  • Set min, max and step so the check and the spin buttons agree.

When not to use

A number field is for amounts, not for any value made of digits.

  • A postal code, a card or a one-time code: a text field with inputmode="numeric".
  • An approximate value on a scale: a range.

Do and don't

Do A number field for a quantity, with its limits in the hint.
Don't A number field for a postal code: it is not a quantity, and its spin buttons would add one to it.

Writing the content

  • Label it for what is counted: "Quantity", "Guests".
  • Put the unit in the label or the hint: "Weight, in kg".
  • The error says the limits: "Enter a number from 1 to 10."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Date

A field for a calendar date, with the browser's own picker. The value is always year-month-day.

Pick a day in 2026.

Code

Copy the markup

A text field with type="date"; min and max limit the days:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="date">Start date</label>
  <input class="luz-input" id="date" type="date" min="2026-01-01" max="2026-12-31" aria-describedby="date-hint">
  <div class="luz-field__helper" id="date-hint">Pick a day in 2026.</div>
</div>

Variants and options

  • min, max: days outside them are refused and greyed out in the picker.
  • step: the step in days.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="date">. It shows the date in the reader's language and order, and always sends yyyy-mm-dd. The picker is the browser's; its light or dark scheme follows the page's color mode.

Without JavaScript

The field works and submits with its form. If a browser you support has no date picker, it shows a text field: say the format in the hint.

Keyboard and ARIA

  • The keys of the browser's own picker apply.
  • The label is the name; the hint says the limits.

Contrast and focus are in the Design tab.

Design

When to use

Use a date field for a day the reader can pick from a calendar, near today.

  • A booking, a deadline or a delivery day.
  • Set min and max so the picker only offers valid days.

When not to use

Scrolling a calendar is slow for a date far from today.

  • A birth date, or any date decades back: three fields, a number for the day, a select for the month and a number for the year.
  • A month and a year alone: a month field.

Do and don't

Do A date field with its limits in the hint.
Don't A text field for a date: each reader guesses the order of day and month.

Writing the content

  • Label it for what the date is: "Start date", "Delivery date".
  • The hint says the limits, not the format: the picker has one.
  • The error says what to change: "Pick a day in 2026."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Time

A field for a time of day, in the reader's 12 or 24 hour clock, as hours and minutes.

Your local time.

Code

Copy the markup

A text field with type="time":

HTML
<div class="luz-field">
  <label class="luz-field__label" for="time">Reminder</label>
  <input class="luz-input" id="time" type="time" value="09:00" aria-describedby="time-hint">
  <div class="luz-field__helper" id="time-hint">Your local time.</div>
</div>

Variants and options

  • min, max: the earliest and the latest time.
  • step: in seconds; step="1" adds seconds, step="900" steps by a quarter of an hour.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="time">. It shows the time on the reader's clock, 12 or 24 hours, and always sends hh:mm. The value has no time zone.

Without JavaScript

The field works and submits with its form. If a browser you support has no time picker, it shows a text field: say the format in the hint.

Keyboard and ARIA

  • The keys of the browser's own picker apply.
  • The label is the name; the hint says the time zone.

Contrast and focus are in the Design tab.

Design

When to use

Use a time field for a time of day: opening hours, an alarm, an appointment.

  • Say the time zone in the hint: the field has none.
  • Use two fields, a date and a time, when the reader may choose them apart.

When not to use

A time field is a time of day, not a length of time.

  • A duration such as 90 minutes: a number field with its unit in the label.
  • A day and a time together: date and time.

Do and don't

Do Say which time zone the time is in.
Don't A text field for a time: 9, 9am and 09:00 mean the same to the reader and not to your code.

Writing the content

  • Label it for what the time is: "Opening time", "Reminder".
  • The hint says the time zone: "Madrid time."
  • The error says the limits: "Choose a time between 09:00 and 18:00."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Date and time

A field for a day and a time together. The value has no time zone.

Madrid time.

Code

Copy the markup

A text field with type="datetime-local"; say the time zone in the hint:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="datetime-local">Meeting starts</label>
  <input class="luz-input" id="datetime-local" type="datetime-local" min="2026-01-01T08:00" aria-describedby="datetime-local-hint">
  <div class="luz-field__helper" id="datetime-local-hint">Madrid time.</div>
</div>

Variants and options

  • min, max: the earliest and the latest moment, as yyyy-mm-ddThh:mm.
  • step: in seconds, as in the time field.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="datetime-local">. "Local" means the reader's own clock: it sends yyyy-mm-ddThh:mm with no time zone. Say the zone in the hint, or store the zone with the value on the server.

Without JavaScript

The field works and submits with its form. If a browser you support has no picker for it, it shows a text field: say the format in the hint.

Keyboard and ARIA

  • The keys of the browser's own picker apply.
  • The label is the name; the hint says the time zone.

Contrast and focus are in the Design tab.

Design

When to use

Use a date and time field for one moment the reader picks, in one place.

  • The start of an event, a booking or a reminder.
  • Say the time zone in the hint: the value has none.

When not to use

The value has no time zone, so it cannot say what moment an event is for readers in other zones.

  • A day alone: a date field. A time alone: a time field.
  • A short list of slots: radio buttons with the times.

Do and don't

Do Say which time zone the moment is in.
Don't A hint that leaves the time zone out: a reader in another zone reads a different moment.

Writing the content

  • Label it for the moment: "Meeting starts", "Delivery by".
  • The hint names the time zone: "Madrid time."
  • The error says what to change: "Pick a moment after 8:00."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Month

A field for a month and a year, such as a card's expiry. The value is year-month.

Month and year, such as 2026-06.

Code

Copy the markup

A text field with type="month". The placeholder and the pattern are for a browser that shows a text field instead:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="month">Billing month</label>
  <input class="luz-input" id="month" type="month" min="2026-01" max="2026-12" placeholder="YYYY-MM" pattern="[0-9]{4}-[0-9]{2}" aria-describedby="month-hint">
  <div class="luz-field__helper" id="month-hint">Month and year, such as 2026-06.</div>
</div>

Variants and options

  • min, max: the first and the last month, as yyyy-mm.
  • step: in months.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="month"> that sends yyyy-mm. Check the browsers you support: one with no month picker shows a text field. A browser with a picker ignores placeholder and pattern, and one without uses them.

Without JavaScript

The field works and submits with its form, as a picker or as a text field.

Keyboard and ARIA

  • The keys of the browser's own picker apply; in a text field, type yyyy-mm.
  • The label is the name; the hint says the format, for the browsers with a text field.

Contrast and focus are in the Design tab.

Design

When to use

Use a month field when only the month and the year matter.

  • A card's expiry, a billing month or a report period.
  • Say the format in the hint, for the browsers that show a text field.

When not to use

A month field has no day, so it cannot hold a date.

  • A day: a date field.
  • A short list of months, for a reader whose browser has no picker: a select for the month and a number for the year.

Do and don't

Do Ask for a month and a year when no day is needed.
Don't A date field when only the month matters: it asks for a day the reader may not have.

Writing the content

  • Label it for what the month is: "Billing month", "Expiry".
  • The hint gives the format: "Month and year, such as 2026-06."
  • The error says what to change: "Enter a month and a year, such as 2026-06."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Week

A field for a week of a year, such as 2026-W24, for planning by week.

Week of the year, such as 2026-W24. Weeks start on Monday.

Code

Copy the markup

A text field with type="week". The placeholder and the pattern are for a browser that shows a text field instead:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="week">Sprint week</label>
  <input class="luz-input" id="week" type="week" min="2026-W01" max="2026-W52" placeholder="YYYY-Www" pattern="[0-9]{4}-W[0-9]{2}" aria-describedby="week-hint">
  <div class="luz-field__helper" id="week-hint">Week of the year, such as 2026-W24. Weeks start on Monday.</div>
</div>

Variants and options

  • min, max: the first and the last week, as yyyy-Www.
  • step: in weeks.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <input type="week"> that sends yyyy-Www, with the weeks counted as ISO 8601 does: they start on Monday. Check the browsers you support: one with no week picker shows a text field, and uses placeholder and pattern.

Without JavaScript

The field works and submits with its form, as a picker or as a text field.

Keyboard and ARIA

  • The keys of the browser's own picker apply; in a text field, type yyyy-Www.
  • The label is the name; the hint says how weeks are counted.

Contrast and focus are in the Design tab.

Design

When to use

Use a week field when the reader plans or reports by the week of the year.

  • A timesheet, a sprint or a delivery schedule.
  • Say in the hint that weeks start on Monday.

When not to use

Most readers do not know which week of the year it is.

  • A day: a date field, and work out the week on the server.
  • A span of days: two date fields, a start and an end.

Do and don't

Do Say how weeks are counted: they start on Monday.
Don't Ask a general audience for a week number: few readers know it.

Writing the content

  • Label it for what the week is: "Sprint week", "Delivery week".
  • The hint gives the format and the first day of the week.
  • The error says what to change: "Enter a week, such as 2026-W24."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Color

A swatch that opens the system's color picker. The value is a hex color, #rrggbb.

Code

Copy the markup

The class .luz-input is what draws the swatch: a bare color input keeps the browser's own look.

HTML
<div class="luz-field">
  <label class="luz-field__label" for="color">Accent colour</label>
  <input class="luz-input" id="color" type="color" value="#0066cf">
</div>

Variants and options

  • value: the starting color. With none, the swatch is black (#000000).
  • disabled: faded, skipped by Tab, not sent.
  • The classes and the error are the Text field's.

How it works

A native <input type="color"> with .luz-input[type="color"]: a 64 px swatch with the field's bottom border. The value is always lowercase hex, #rrggbb, with no transparency and no name. The picker is the system's.

Without JavaScript

The field works and submits its hex value with the form.

Keyboard and ARIA

  • Tab moves focus to the swatch. Enter or Space opens the picker, whose keys are the system's.
  • The label is the name. A swatch is a color alone: write the value as text where the reader needs it.

Contrast and focus are in the Design tab.

Design

When to use

Use a color field when the reader may choose any color: a label, a tag or a chart.

  • Start with value, so the swatch is not black.
  • Check the contrast of the color on your side: the picker allows any.

When not to use

A color field is for a free choice, not for a short list.

  • A few named colors: radio buttons with the names.
  • A color your design fixes: do not ask the reader.

Do and don't

Do A color field for a free choice, with a label that says what it colors.
Don't A color field to choose among three colors: radio buttons with their names say which is which.

Writing the content

  • Label it for what it colors: "Accent color", "Label color".
  • Name the color where it matters: a swatch alone says nothing to some readers.
  • The error says what to change: "Choose a darker color."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

File

A button that opens the system's file chooser, and the name of the chosen file.

PDF or PNG, up to 5 MB.

Code

Copy the markup

The class .luz-input styles the button and the name. The form needs enctype="multipart/form-data" to send the file:

HTML
<form method="post" enctype="multipart/form-data">
  <div class="luz-field">
    <label class="luz-field__label" for="file">Attachment</label>
    <input class="luz-input" id="file" type="file" accept=".pdf,.png" aria-describedby="file-hint">
    <div class="luz-field__helper" id="file-hint">PDF or PNG, up to 5 MB.</div>
  </div>
  <button type="submit">Upload</button>
</form>

Variants and options

  • accept: the types the chooser offers, such as .pdf or image/*.
  • multiple: several files; required: the browser stops the form until one is chosen.
  • The error and disabled states are the Text field's.

How it works

A native <input type="file">. .luz-input[type="file"] styles the button with tokens (::file-selector-button). Its words, "Choose file" and "No file chosen", are the browser's, in the reader's language. accept only filters the chooser: check the type and the size on the server.

Without JavaScript

The field works and sends its file with the form, with method="post" and enctype="multipart/form-data".

Keyboard and ARIA

  • Tab moves focus to the button. Enter or Space opens the chooser.
  • The label is the name; the hint says the types and the size.

Contrast and focus are in the Design tab.

Design

When to use

Use a file field when the reader sends a file from their own device.

  • A résumé, a photo or an attachment.
  • Say the accepted types and the size in the hint, before the reader chooses.

When not to use

A file field cannot check what it receives.

  • Do not rely on accept for safety: it filters the chooser, and a reader can pick anything. Check on the server.
  • The address of a file that is already online: a URL field.

Do and don't

Do Say the types and the size in the hint, before the reader chooses.
Don't A file field with no hint: the reader finds out about the limits when the upload fails.

Writing the content

  • Label it with a noun: "Attachment", "Photo".
  • The hint gives the types and the size: "PDF or PNG, up to 5 MB."
  • The error says what to change: "The file is larger than 5 MB. Choose a smaller one."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Range

A slider for a value on a scale, the place of the thumb matters more than the exact number.

Code

Copy the markup

A slider with min, max and step, and an <output> that shows its value. The slider shows no number of its own, so the output is updated by a few lines of your own script:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="volume">Volume <output class="luz-output" id="volume-out" for="volume">50</output></label>
  <input class="luz-input" id="volume" type="range" min="0" max="100" value="50">
</div>
JS
const range = document.getElementById("volume");
const output = document.getElementById("volume-out");
range.addEventListener("input", () => {
  output.value = range.value;
});

Variants and options

  • min, max, step: the scale and the size of a step.
  • disabled: faded, skipped by Tab, not sent. A range has no read-only state.
  • With no <output> the reader cannot see the number: always add one.

How it works

A native <input type="range">. .luz-input[type="range"] sets accent-color to --luz-brand-primary, so the thumb and the filled track follow the brand. LUZ has no script for the output: yours listens to the input event.

Without JavaScript

The slider works and submits its value. Without your script the output keeps showing the starting value.

Keyboard and ARIA

  • Tab moves focus to the slider. Left and Down take away one step, Right and Up add one. Home and End go to the ends.
  • The label is the name; tie the output to the slider with for.

Contrast and focus are in the Design tab.

Design

When to use

Use a range when the reader sets a value by feel and a near answer is enough.

  • Volume, brightness or a rough budget.
  • Show the value next to the slider, and set min, max and step to what you need.

When not to use

Do not use a range when the exact number matters or the scale is long.

  • An exact amount: a number field.
  • A few named levels: radio buttons with the names.

Do and don't

Do Show the value as text next to the slider.
Don't A slider with no value: the reader cannot tell what they set.

Writing the content

  • Label it for what it sets: "Volume", "Brightness".
  • The output holds the number only; put the unit in the label: "Volume, %".
  • Say the ends of the scale in the hint if they are not obvious.

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Textarea

A field for text of more than one line, such as a message or a description.

Up to 500 characters.

Code

Copy the markup

A native <textarea>. .luz-input--textarea gives it its height, and maxlength its limit:

HTML
<div class="luz-field">
  <label class="luz-field__label" for="textarea">Message</label>
  <textarea class="luz-input luz-input--textarea" id="textarea" rows="4" maxlength="500" aria-describedby="textarea-hint"></textarea>
  <div class="luz-field__helper" id="textarea-hint">Up to 500 characters.</div>
</div>

Variants and options

  • rows: the starting height; the reader drags it taller, never wider.
  • maxlength, minlength: the browser checks them; say the limit in the hint.
  • The states are the Text field's: error, required, disabled and read-only.

How it works

A native <textarea> styled like a text field, at least 80 px high. A textarea with no class looks the same. LUZ has no character counter.

Without JavaScript

The field works and submits with its form.

Keyboard and ARIA

  • Tab moves focus in and out. Enter adds a new line: it does not send the form.
  • The label is the name; the hint says the limit.

Contrast and focus are in the Design tab.

Design

When to use

Use a textarea for free text of more than one line.

  • A message, a comment, a description or a bio.
  • Say the limit in the hint, if there is one.

When not to use

Do not use a textarea for a short answer, nor for formatted writing.

  • A name, a title or a reference: a text field.
  • Text with headings and links: your own editor. Never build a textarea from a <div contenteditable>.

Do and don't

Do A textarea for a message, with its limit in the hint.
Don't A textarea for a name: it invites an essay and takes the room of a paragraph.

Writing the content

  • Label it for what to write: "Message", "Description".
  • The hint gives the limit: "Up to 500 characters."
  • The error says what to change: "Write at least 20 characters."

Contrast and focus

Shared with the other inputs: contrast and focus of inputs.

Tokens it uses

Shared with the other inputs: tokens of inputs.

Keyboard and ARIA are in the Code tab.

Contrast and focus of inputs

The label and the hint are secondary text on the page; what the reader types is primary text on the field. The range slider and the file button have their own pairs.

PartColour on backgroundLightDarkHigh contrastNeeds
Label
--luz-text-secondary on --luz-surface-page
6.2:1
7.5:1
19.3:1
4.5:1
Typed text
--luz-text-primary on --luz-input-bg
16.5:1
14.0:1
21.0:1
4.5:1
Placeholder
--luz-text-muted on --luz-input-bg
5.1:1
5.2:1
9.0:1
4.5:1
Error message
--luz-color-danger on --luz-surface-page
5.3:1
11.7:1
9.3:1
4.5:1
Field bottom border and color swatch edge
--luz-input-border on --luz-input-bg
3.9:1
3.9:1
21.0:1
3:1
Error border
--luz-color-danger on --luz-input-bg
5.5:1
10.8:1
9.3:1
3:1
Disabled text
--luz-text-disabled on --luz-input-bg
3.7:1
3.9:1
5.3:1
3:1
Range thumb and filled track
--luz-brand-primary on --luz-surface-page
5.6:1
7.7:1
14.1:1
3:1
File name
--luz-text-secondary on --luz-input-bg
6.4:1
6.9:1
19.3:1
4.5:1
File button text
--luz-text-primary on --luz-surface-strong
13.5:1
10.9:1
18.0:1
4.5:1
File button text, hovered
--luz-text-primary on --luz-surface-hover
14.4:1
12.6:1
20.1:1
4.5:1
Focus ring
--luz-focus-ring on --luz-surface-page
5.6:1
7.7:1
21.0:1
3:1

Measured from the token values of each mode, rounded down. Needs is the WCAG 2.2 AA minimum: 4.5:1 for text, 3:1 for focus rings, borders and icons.

  • Focus: a 2 px --luz-focus-ring outline, 2 px outside the field (4 px outside a range slider); the bottom border of a field also turns to the ring colour.
  • Not colour alone: an invalid field has a 2 px bottom border and a message in words; a read-only or disabled field has a dashed one. Write the value of a color field as text.
  • Target: a field is 44 px high.

Tokens of inputs

The 25 tokens that these controls' rules read, by token family. Each family links to its page, with the values in every mode.

Spacing and sizing
--luz-control-lg, --luz-control-md, --luz-space-2xs, --luz-space-3xs, --luz-space-md, --luz-space-sm, --luz-space-xs
Typography
--luz-font-sans, --luz-text-control, --luz-text-md, --luz-text-sm
Radius
--luz-radius-md
Motion
--luz-duration-base, --luz-duration-fast

Known issues

None known.