Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
The most versatile form element, accepting text, numbers, dates, files, and more through the type attribute. Always pair with a label for accessibility.
| Category | Types | Purpose |
|---|---|---|
| Text | text, email, password, url, tel, search |
Single-line text input |
| Numeric | number, range |
Numeric values with constraints |
| Date/Time | date, time, datetime-local, month, week |
Temporal values with pickers |
| Selection | checkbox, radio |
Boolean or mutually exclusive choices |
| Other | file, color, hidden |
Specialized inputs |
Single-line text inputs for names, emails, passwords, URLs, phone numbers, and search. Each type triggers the appropriate mobile keyboard and built-in validation.
<input type="text" placeholder="Name" autocomplete="name" /><input type="email" placeholder="Email" autocomplete="email" /><input type="password" autocomplete="current-password" /><input type="url" placeholder="https://" /><input type="tel" placeholder="Phone" autocomplete="tel" /><input type="search" placeholder="Search..." />
Use type="number" for values with increment/decrement controls, and type="range" for slider selection. Constrain values with min, max, and step.
<input type="number" min="1" max="99" step="1" value="1" /><input type="number" min="0" step="0.01" value="9.99" /><input type="range" min="0" max="100" value="50" />
Native date/time pickers vary by browser. Consider progressive enhancement. Use min and max to constrain date ranges.
<input type="date" /><input type="time" /><input type="datetime-local" /><input type="month" /><input type="week" /> <!-- With date constraints --><input type="date" min="2024-01-01" max="2024-12-31" />
Use type="checkbox" for boolean or multi-select choices, and type="radio" for mutually exclusive selections within a group (same name). Always wrap radio groups in a fieldset for accessibility.
<!-- Checkbox --><label> <input type="checkbox" name="subscribe" /> Subscribe to newsletter</label> <!-- Radio group (wrap in fieldset) --><fieldset> <legend>Select size</legend> <label> <input type="radio" name="size" value="small" /> Small </label> <label> <input type="radio" name="size" value="medium" checked /> Medium </label> <label> <input type="radio" name="size" value="large" /> Large </label></fieldset>
File upload. Use accept to restrict file types and multiple for multiple files. See also the data-upload enhancement below.
Color picker returning a hex value. See also the data-color enhancement below.
Invisible form data. Used for tokens, IDs, or tracking.
<input type="hidden" name="csrf_token" value="abc123" /><input type="hidden" name="user_id" value="42" />
<input type="file" accept=".pdf,.doc" /><input type="file" accept="image/*" multiple /input type="color" value="#4a90d9" /><input type="hidden" name="token" value="..." />
Inputs support disabled, readonly, required, and invalid states. Disabled inputs prevent interaction and are not submitted with the form. Readonly inputs allow selection but not editing, and their values are submitted.
<input type="text" disabled value="Cannot edit" /><input type="text" readonly value="Can select but not edit" /> <!-- Required --><input type="text" required /> <!-- Invalid state --><input type="email" aria-invalid="true" value="not-an-email" /> <!-- Pattern validation --><input type="text" pattern="[a-zA-Z0-9_]+" title="Letters, numbers, and underscores only" />
| Attribute | Purpose | Example |
|---|---|---|
required |
Field must have a value | <input required /> |
minlength |
Minimum character count | minlength="2" |
maxlength |
Maximum character count | maxlength="100" |
min |
Minimum numeric/date value | min="0" |
max |
Maximum numeric/date value | max="100" |
step |
Increment for numeric values | step="0.01" |
pattern |
Regex pattern for validation | pattern="[A-Za-z]+" |
type |
Built-in validation (email, url) | type="email" |
Add autocomplete suggestions using datalist.
<input type="text" list="browsers" /><datalist id="browsers"> <option value="Chrome"></option> <option value="Firefox"></option></datalist>
Use the form-field custom element for CSS-only validation feedback.
<form-field> <label for="ff-email">Email</label> <input type="email" id="ff-email" name="email" required autocomplete="email" aria-describedby="ff-email-msg" /> <output id="ff-email-msg" for="ff-email" aria-live="polite"> Enter a valid email address </output></form-field>
Help browsers autofill forms correctly with specific autocomplete values.
| Field Type | Autocomplete Value |
|---|---|
| Full name | name |
email |
|
| Phone | tel |
| Street address | street-address |
| City | address-level2 |
| State/Province | address-level1 |
| Postal code | postal-code |
| Country | country-name |
| Credit card number | cc-number |
| Current password | current-password |
| New password | new-password |
| One-time code | one-time-code |
<input type="text" name="name" autocomplete="name" /><input type="email" name="email" autocomplete="email" /><input type="password" autocomplete="current-password" /><input type="password" autocomplete="new-password" />
Add data-switch to a checkbox for on/off toggle switch styling. Uses CSS appearance: none with a sliding knob. JS adds role="switch" for screen readers.
<!-- Small --><label> <input type="checkbox" data-switch="sm" name="compact"> Compact mode</label> <!-- Default --><label> <input type="checkbox" data-switch name="default"> Default size</label> <!-- Large --><label> <input type="checkbox" data-switch="lg" name="feature"> Large switch</label>
| Attribute | Values | Description |
|---|---|---|
data-switch |
"", "sm", "lg" |
Enable switch styling with optional size variant. |
Add data-strength and data-rules to a password input inside <form-field> to show a real-time strength meter and rules checklist. Works alongside the existing password show/hide toggle.
<form-field> <label for="pw">Password</label> <input type="password" id="pw" data-strength data-rules="length:8,uppercase,lowercase,number,special" placeholder="Create a strong password" autocomplete="new-password" /></form-field>
| Attribute | Description |
|---|---|
data-strength |
Enable strength meter and rules checklist. |
data-rules |
Comma-separated rules: length:N, uppercase, lowercase, number, special. |
Add data-range to a range input for cross-browser styled track and thumb. Add data-bubble for a floating value display. Pair with <datalist> for tick labels.
<input type="range" min="0" max="1000" step="100" list="prices" data-range data-bubble data-prefix="$"><datalist id="prices"> <option value="0" label="$0"> <option value="500" label="$500"> <option value="1000" label="$1K"></datalist>
| Attribute | Description |
|---|---|
data-range |
Enable cross-browser track/thumb styling and fill. |
data-bubble |
Show floating value bubble above the thumb. |
data-prefix |
Text before the value (e.g., "$"). |
data-suffix |
Text after the value (e.g., "%", "°F"). |
Add data-upload to a file input to wrap it in a drag-and-drop zone with file list display.
<input type="file" data-upload accept=".pdf,.doc" multiple>
| Attribute | Description |
|---|---|
data-upload |
Enable drop zone enhancement on a file input. |
The native accept and multiple attributes work as normal. Without JS, the standard file input is shown.
Add data-mask to a text input to format the value as the user types. Preset masks handle common patterns; use data-mask="custom" with data-pattern for arbitrary formats.
<!-- Preset masks --><input type="text" data-mask="phone" placeholder="(555) 123-4567"><input type="text" data-mask="credit-card" placeholder="1234 5678 9012 3456"><input type="text" data-mask="date" placeholder="MM/DD/YYYY"><input type="text" data-mask="ssn" placeholder="123-45-6789"><input type="text" data-mask="zip" placeholder="12345"> <!-- Custom mask: # = digit, A = letter, * = any --><input type="text" data-mask="custom" data-pattern="AA-####" placeholder="AB-1234">
| Value | Pattern | Example |
|---|---|---|
phone | (###) ###-#### | (555) 123-4567 |
credit-card | #### #### #### #### | 1234 5678 9012 3456 |
date | ##/##/#### | 01/15/2026 |
ssn | ###-##-#### | 123-45-6789 |
zip | ##### | 90210 |
| Token | Accepts |
|---|---|
# | Any digit (0-9) |
A | Any letter (a-z, A-Z) |
* | Any alphanumeric character |
| Attribute | Description |
|---|---|
data-mask | Mask type: phone, credit-card, date, ssn, zip, or custom. |
data-pattern | Custom pattern string when data-mask="custom". |
inputmode="numeric" for digit-only masksmaxlength from the pattern lengthdataset.rawValue for form processingAdd data-markers to a data-range input to display tick marks at each step value. Combines with data-bubble and <datalist> labels.
<input type="range" min="1" max="5" step="1" value="3" data-range data-bubble data-markers>
| Attribute | Description |
|---|---|
data-markers |
Show tick marks at each step position along the track. |
Wrap checkboxes in a <fieldset data-toggle-tags> for pill-chip styling. Pure CSS — no JS needed. Add data-max to limit the number of selections.
<fieldset data-toggle-tags> <legend>Favorite Topics</legend> <label><input type="checkbox" name="topics" value="ai"> AI / ML</label> <label><input type="checkbox" name="topics" value="web"> Web Dev</label> <label><input type="checkbox" name="topics" value="mobile"> Mobile</label></fieldset> <!-- With max selection limit --><fieldset data-toggle-tags data-layout-max="3"> <legend>Pick up to 3</legend> <label><input type="checkbox" name="skills" value="js"> JavaScript</label> <label><input type="checkbox" name="skills" value="py"> Python</label> <label><input type="checkbox" name="skills" value="go"> Go</label> <label><input type="checkbox" name="skills" value="rust"> Rust</label></fieldset>
| Attribute | Description |
|---|---|
data-toggle-tags |
Enable pill-chip styling on a checkbox fieldset. |
data-max |
Maximum number of selections. Unchecked boxes are disabled at the limit. |
Native checkboxes give you form participation, validation, reset, keyboard navigation, and accessibility for free. The visual transformation uses :has(:checked) to style labels. JS is only loaded when data-max is set.
Add data-stepper to a number input for custom +/− buttons. Hides native spinners and disables buttons at min/max boundaries.
<input type="number" min="0" max="50" step="1" value="1" data-stepper> <!-- Decimal steps --><input type="number" min="0" max="10" step="0.5" value="1.0" data-stepper>
| Attribute | Description |
|---|---|
data-stepper |
Enable +/− stepper buttons on a number input. |
Without JS, the native number input with browser spinners works fine.
Add data-color to a color input for a styled swatch circle and hex code display. Clicking opens the native color picker.
<input type="color" value="#6366f1" data-color>
| Attribute | Description |
|---|---|
data-color |
Enable swatch + hex display enhancement on a color input. |
Without JS, the native color picker renders (functional but unstyled).
aria-describedby to link error text to inputsaria-invalid="true" for explicit invalid state/* Form control tokens (defined in src/tokens/forms.css) */:root { --input-height: var(--size-touch-min); --input-padding-inline: var(--size-s); --input-bg: var(--color-surface); --input-border: var(--color-border); --input-border-focus: var(--color-interactive); --input-radius: var(--radius-m); --input-text: inherit; --input-placeholder: var(--color-text-muted); --control-size: 1.125rem; --control-border: var(--color-border-strong); --control-checked-bg: var(--color-primary); --control-checked-border: var(--color-primary); --control-radius-check: var(--radius-s); --control-radius-radio: var(--radius-full); --select-chevron: url("data:image/svg+xml,..."); --range-track-h: 0.375rem; --range-track-bg: var(--color-border); --range-track-fill: var(--color-primary); --range-thumb-size: 1.25rem; --range-thumb-bg: var(--color-surface); --range-thumb-border: var(--color-primary);} /* Text inputs, select, textarea — token-driven */input:not(:is([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="submit"], [type="button"], [type="reset"])),textarea, select { border: var(--border-width-thin) solid var(--input-border); border-radius: var(--input-radius); background: var(--input-bg); color: var(--input-text); min-block-size: var(--input-height);} /* Checkbox & radio — appearance: none + pseudo-elements */input[type="checkbox"]:not([data-switch]),input[type="radio"] { appearance: none; width: var(--control-size); height: var(--control-size); border: var(--border-width-medium) solid var(--control-border);} input[type="checkbox"]:not([data-switch]):checked,input[type="radio"]:checked { background: var(--control-checked-bg); border-color: var(--control-checked-border);} /* Checkmark via clip-path (zero SVG assets) */input[type="checkbox"]:not([data-switch]):checked::after { clip-path: polygon(14% 50%, 8% 60%, 38% 86%, 94% 20%, 86% 12%, 38% 70%);} /* Radio dot */input[type="radio"]:checked::after { inset: 3px; border-radius: 50%;} /* Range — token-driven track and thumb */input[type="range"] { appearance: none; background: linear-gradient(to right, var(--range-track-fill) var(--_pct, 50%), var(--range-track-bg) var(--_pct, 50%));}
method="dialog"