Form
Native controls styled with tokens. Every control needs a visible <label>; help and error text are linked with aria-describedby. No JavaScript, no custom widgets.
<form class="iv-stack" style="max-width: 28rem" action="#" method="get" onsubmit="return false" aria-describedby="demo-form-note">
<p class="iv-u-text-sm iv-u-text-muted iv-u-m-0" id="demo-form-note">Demo form: nothing is submitted.</p>
<div class="iv-field">
<label class="iv-label" for="f-name">Full name <span class="iv-label__required" aria-hidden="true">*</span></label>
<input class="iv-input" id="f-name" name="name" type="text" required autocomplete="name" aria-describedby="f-name-help">
<p class="iv-field__help iv-u-m-0" id="f-name-help">As it should appear on the invoice.</p>
</div>
<div class="iv-field">
<label class="iv-label" for="f-plan">Plan</label>
<select class="iv-select" id="f-plan" name="plan">
<option>Starter</option><option>Team</option><option>Enterprise</option>
</select>
</div>
<div class="iv-field">
<label class="iv-label" for="f-notes">Notes</label>
<textarea class="iv-input" id="f-notes" name="notes" rows="3"></textarea>
</div>
<fieldset class="iv-fieldset">
<legend class="iv-fieldset__legend">Notifications</legend>
<div class="iv-checks">
<label class="iv-check"><input type="checkbox" name="n" value="product" checked> Product updates</label>
<label class="iv-check"><input type="checkbox" name="n" value="security"> Security alerts</label>
</div>
</fieldset>
<fieldset class="iv-fieldset">
<legend class="iv-fieldset__legend">Billing</legend>
<div class="iv-checks iv-checks--inline">
<label class="iv-check"><input type="radio" name="billing" value="monthly" checked> Monthly</label>
<label class="iv-check"><input type="radio" name="billing" value="yearly"> Yearly</label>
</div>
</fieldset>
<div class="iv-cluster">
<button class="iv-button iv-button--primary" type="submit">Save (demo)</button>
<button class="iv-button iv-button--ghost" type="reset">Reset</button>
</div>
</form>Error and disabled
Mark invalid fields with aria-invalid="true" and point aria-describedby at the iv-field__error element. Disabled controls use the native attribute.
<div class="iv-stack" style="max-width: 28rem">
<div class="iv-field">
<label class="iv-label" for="f-email">Work email</label>
<input class="iv-input" id="f-email" type="email" value="dave@" aria-invalid="true" aria-describedby="f-email-error" autocomplete="email">
<p class="iv-field__error iv-u-m-0" id="f-email-error">Enter a complete email address, like [email protected].</p>
</div>
<div class="iv-field">
<label class="iv-label" for="f-locked">Plan</label>
<input class="iv-input" id="f-locked" type="text" value="Team (locked)" disabled>
</div>
</div>Floating labels and input groups
iv-field--float keeps the label inside the control until it has focus or a value (give the control placeholder=" "). iv-input-group joins prefixes, suffixes and buttons to a control.
<div class="iv-stack" style="max-width: 30rem">
<p class="iv-u-text-sm iv-u-text-muted iv-u-m-0">Demo fields with made-up values. Nothing is submitted; the CSS here needs no JavaScript.</p>
<div class="iv-field iv-field--float">
<input class="iv-input" id="fl-email" name="email" type="email" placeholder=" " autocomplete="email">
<label class="iv-label" for="fl-email">Work email</label>
</div>
<div class="iv-field iv-field--float">
<input class="iv-input" id="fl-company" name="company" type="text" placeholder=" " value="iNTERVOLUTIONS" autocomplete="organization">
<label class="iv-label" for="fl-company">Company</label>
</div>
<div class="iv-field iv-field--float">
<select class="iv-select" id="fl-plan" name="plan">
<option value="">Choose a plan</option>
<option value="starter">Starter</option>
<option value="team">Team</option>
<option value="enterprise">Enterprise</option>
</select>
<label class="iv-label" for="fl-plan">Plan</label>
</div>
<div class="iv-field iv-field--float">
<textarea class="iv-textarea" id="fl-notes" name="notes" placeholder=" "></textarea>
<label class="iv-label" for="fl-notes">Notes for the team</label>
</div>
<div class="iv-field">
<label class="iv-label" for="fg-site">Site address</label>
<div class="iv-input-group">
<span class="iv-input-group__addon">https://</span>
<input class="iv-input" id="fg-site" name="site" type="text" value="intervolutions" autocomplete="off">
<span class="iv-input-group__addon">.com</span>
</div>
</div>
<div class="iv-field">
<label class="iv-label" for="fg-key">API key</label>
<div class="iv-input-group">
<input class="iv-input" id="fg-key" name="key" type="text" value="iv_demo_8f2c" readonly autocomplete="off">
<button class="iv-input-group__addon iv-input-group__addon--button" type="button">Copy</button>
</div>
<p class="iv-field__help iv-u-m-0">Made-up key. The button is a demo: it copies nothing.</p>
</div>
</div>Switch, range, file and sizes
A checkbox becomes a switch, a range input gets a filled track, a file input becomes a drop zone that keeps the real input accessible, textareas grow with their content where the browser supports it, and inputs come in three sizes like buttons.
<div class="iv-stack" style="max-width: 32rem">
<p class="iv-u-text-sm iv-u-text-muted iv-u-m-0">Demo controls with made-up values. Nothing is submitted; none of this needs JavaScript.</p>
<fieldset class="iv-fieldset">
<legend class="iv-fieldset__legend">Notifications</legend>
<div class="iv-checks">
<label class="iv-switch">
<input type="checkbox" name="deploys" role="switch" checked>
<span class="iv-switch__track"><span class="iv-switch__thumb"></span></span>
<span>Email me about deploys</span>
</label>
<label class="iv-switch">
<input type="checkbox" name="digest" role="switch">
<span class="iv-switch__track"><span class="iv-switch__thumb"></span></span>
<span>Weekly digest</span>
</label>
<label class="iv-switch">
<input type="checkbox" name="sms" role="switch" disabled>
<span class="iv-switch__track"><span class="iv-switch__thumb"></span></span>
<span>SMS alerts (not available in this demo)</span>
</label>
</div>
</fieldset>
<div class="iv-field">
<label class="iv-label" for="ct-budget">Monthly budget: 620 €</label>
<input class="iv-range" id="ct-budget" name="budget" type="range" min="0" max="1000" step="20" value="620" style="--iv-range-value: 62%">
<p class="iv-field__help iv-u-m-0">The filled part comes from <code>--iv-range-value</code>, set here in the markup. Moving the slider does not update it without JavaScript.</p>
</div>
<div class="iv-field">
<label class="iv-label" for="ct-file">Brand assets</label>
<label class="iv-file">
<input type="file" id="ct-file" name="assets" multiple accept="image/*">
<span class="iv-file__zone">
<span>Choose files or drop them here</span>
<span class="iv-u-text-sm iv-u-text-muted">PNG or SVG, up to 5 MB each. Demo only: nothing is uploaded.</span>
</span>
</label>
</div>
<div class="iv-field">
<label class="iv-label" for="ct-notes">Release notes</label>
<textarea class="iv-textarea" id="ct-notes" name="notes" placeholder="What changed?">The box grows with the text where the browser supports field-sizing, and stays resizable everywhere else.</textarea>
</div>
<div class="iv-field">
<label class="iv-label" for="ct-small">Coupon (small)</label>
<input class="iv-input iv-input--sm" id="ct-small" name="coupon" type="text" value="IVOLT-25" autocomplete="off">
</div>
<div class="iv-field">
<label class="iv-label" for="ct-medium">Project (default)</label>
<input class="iv-input" id="ct-medium" name="project" type="text" value="Spectacular forms" autocomplete="off">
</div>
<div class="iv-field">
<label class="iv-label" for="ct-large">Headline (large)</label>
<input class="iv-input iv-input--lg" id="ct-large" name="headline" type="text" value="Forms with character" autocomplete="off">
</div>
<div class="iv-cluster">
<button class="iv-button iv-button--primary iv-button--lg" type="button">Large button</button>
<button class="iv-button" type="button">Default</button>
<button class="iv-button iv-button--sm" type="button">Small</button>
</div>
</div>.iv-root {
--iv-range-value: 62%;
}Classes
| Class | Element |
|---|---|
iv-field | Wrapper: label, control, help/error stacked. |
iv-label, iv-label__required | <label> and the required marker (keep required on the control). |
iv-input | <input> (text-like types) and <textarea>. |
iv-select | Native <select>. |
iv-check, iv-checks, iv-checks--inline | Label wrapping a checkbox or radio; vertical or inline groups. |
iv-fieldset, iv-fieldset__legend | Grouping with a legend. |
iv-field__help, iv-field__error | Help and error text. |
Common mistakes
- Placeholder used as the only label.
- Error text not referenced by
aria-describedby. - Demo forms that post to a backend that does not exist: say it is a demo, as the examples above do.
Class index
Parts and modifiers of this family that the examples above do not show. The full list per stylesheet lives in the reference.
| Class | What it does |
|---|---|
iv-form__summary-title | Heading of the error summary; its text is the summaryTitle option. |
iv-form__summary-list | List of links to the invalid fields. |
iv-input-group__addon | Prefix or suffix glued to an input: a currency, a unit, a domain. |
iv-input-group__addon--button | Add-on that is a real button and keeps its focus ring. |