form

Container for interactive form controls that collects and submits user data. The form element provides structure, validation, and submission handling.

When to Use

  • Collecting user input (contact forms, login, registration)
  • Search functionality
  • Any interaction that submits data to a server
  • Client-side form validation with HTML5 attributes

Basic Usage

A minimal form with essential attributes.

Code

<form action="/api/submit" method="POST"> <layout-stack data-layout-gap="m"> <div class="group"> <label for="name">Name</label> <input type="text" id="name" name="name" required /> </div> <div class="group"> <label for="email">Email</label> <input type="email" id="email" name="email" required /> </div> <button type="submit">Submit</button> </layout-stack> </form>

Form Attributes

Attribute Purpose Example
action URL to submit data to action="/api/contact"
method HTTP method (GET or POST) method="POST"
enctype Encoding type for POST data enctype="multipart/form-data"
novalidate Disable browser validation novalidate
autocomplete Enable/disable autofill autocomplete="on"
name Form name for scripting name="contact-form"

Layout Variants

.stacked (Vertical Layout)

Fields arranged vertically with consistent spacing. Best for general-purpose forms.

.inline (Horizontal Layout)

Fields arranged horizontally. Best for search bars and compact forms.

.grid (Two-Column Layout)

Labels on the left, inputs on the right. Best for data entry forms.

Code

<!-- Vertical stacked layout --> <form class="stacked"> <div class="group">...</div> </form> <!-- Horizontal inline layout --> <form class="inline"> <input type="search" /> <button type="submit">Search</button> </form> <!-- Two-column grid layout --> <form class="grid"> <label for="name">Name</label> <input type="text" id="name" /> </form>

Form Groups

The .group class wraps a label-input pair with appropriate spacing.

.group (Vertical)

.group.horizontal

For checkbox and toggle layouts where label and input are side by side.

Code

<!-- Vertical group --> <div class="group"> <label for="field">Label</label> <input type="text" id="field" /> </div> <!-- Horizontal group --> <div class="group horizontal"> <input type="checkbox" id="remember" /> <label for="remember">Remember me</label> </div>

Form Actions

The .actions class provides a container for submit/cancel buttons.

Default (start aligned)
.actions.end
.actions.between

Code

<div class="actions">...</div> <!-- start aligned --> <div class="actions end">...</div> <!-- end aligned --> <div class="actions between">...</div> <!-- space between -->

Help and Error Text

Provide additional context with help and error text classes.

Must be at least 8 characters with one number. Please enter a valid email address.

Code

<div class="group"> <label for="password">Password</label> <input type="password" id="password" /> <span class="help">At least 8 characters.</span> </div> <div class="group"> <label for="email">Email</label> <input type="email" id="email" aria-invalid="true" /> <span class="error">Invalid email address.</span> </div>

File Upload Forms

For file uploads, use enctype="multipart/form-data".

Accepts PDF, DOC, or DOCX files up to 10MB.

Code

<form enctype="multipart/form-data" method="POST"> <input type="file" name="document" accept=".pdf,.doc" /> <button type="submit">Upload</button> </form>

Complete Contact Form

A full example combining multiple form elements.

Contact Us

Conditional Fields (data-show-when)

Use data-show-when or data-hide-when on any element inside a form to conditionally display sections based on another field’s value. Hidden elements get hidden and inert attributes, preventing them from blocking validation.

Show When

Show a section when a field has a specific value.

<select name="account-type"> <option value="personal">Personal</option> <option value="business">Business</option> </select> <fieldset data-show-when="account-type=business"> <legend>Business Details</legend> <input name="company" placeholder="Company name"> </fieldset>

Hide When

Hide a section when a condition is met — the inverse of data-show-when.

<label> <input type="checkbox" name="gift" value="yes"> This is a gift </label> <form-field data-hide-when="gift=yes"> <label for="receipt">Send receipt to</label> <input type="email" id="receipt" name="receipt"> </form-field>
Attribute Format Description
data-show-when name=value Show when the named field equals the value.
data-hide-when name=value Hide when the named field equals the value.

Works with <select>, <input type="radio">, <input type="checkbox">, and text inputs. The name refers to the name attribute of the controlling field within the same <form>.

Form Autosave (data-autosave)

Add data-autosave="key" to a <form> to automatically save its data to localStorage as the user types. On page reload, the draft is restored and a “Draft restored” toast appears. Drafts are cleared on submit or reset and expire after 24 hours.

<form data-autosave="contact-form"> <input name="name" placeholder="Name"> <textarea name="message" placeholder="Message"></textarea> <button type="submit">Send</button> </form>
Attribute Description
data-autosave Storage key for this form’s draft. Must be unique per form.

Behavior

  • Save: Debounced (500ms) on every input and change event
  • Restore: On page load if a non-expired draft exists
  • Clear: On form submit or reset
  • Skip: Password and file inputs are never saved
  • Expire: Drafts older than 24 hours are discarded

Accessibility Notes

  • Label all inputs: Every form control needs an associated label
  • Use fieldsets: Group related fields with fieldset and legend
  • Error identification: Use aria-invalid and aria-describedby for errors
  • Autocomplete: Add appropriate autocomplete attributes for autofill
  • Focus management: Use autofocus on the first field (one per page)
  • Submit button: Always include a submit button, not just Enter key handling

CSS Reference

form { display: block; } /* Vertical stacked layout */ form.stacked { display: flex; flex-direction: column; gap: var(--size-m); } /* Horizontal inline layout */ form.inline { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--size-s); } /* Two-column grid layout */ form.grid { display: grid; grid-template-columns: minmax(100px, auto) 1fr; gap: var(--size-s) var(--size-m); align-items: center; } /* Form group wrapper */ .group { display: flex; flex-direction: column; gap: var(--size-2xs); } .group.horizontal { flex-direction: row; align-items: center; gap: var(--size-s); } /* Form actions */ .actions { display: flex; gap: var(--size-s); margin-block-start: var(--size-m); } .actions.end { justify-content: flex-end; } .actions.between { justify-content: space-between; } /* Help and error text */ .help { font-size: var(--font-size-sm); color: var(--color-text-muted); } .error { font-size: var(--font-size-sm); color: oklch(55% 0.2 25); }

Related Elements

  • input - Text inputs, checkboxes, radios, and more
  • button - Form submission and reset buttons
  • select - Dropdown selection menus
  • textarea - Multi-line text input
  • fieldset - Group related form fields
  • label - Accessible input labels
  • form-field - Enhanced form field wrapper