Components
Input group
An input with attached addons (icons, labels, or buttons) on any side.
An .input-group wraps an .input with .input-group__addon elements. Place an addon with
data-align: inline (leading or trailing) or block (above or below).
Default
Loading components…
<div class="field"> <label class="field__label" for="f-search">Search</label> <label class="input-group"> <input class="input" id="f-search" type="search" name="q" inputmode="search" placeholder="Search…" autocomplete="off" spellcheck="false" enterkeyhint="search" aria-describedby="f-search-hint" /> <span class="input-group__addon"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <circle cx="112" cy="112" r="80" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <line x1="168.57" y1="168.57" x2="224" y2="224" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </span> <span class="input-group__addon" data-align="inline-end"> <button class="button" data-variant="ghost" data-size="icon-sm" type="submit" aria-label="Submit search" > <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <rect width="256" height="256" fill="none" /> <line x1="40" y1="128" x2="216" y2="128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <polyline points="144 56 216 128 144 200" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> </span> </label> <div class="field__description"> <span class="field__hint" id="f-search-hint">Search across every page in the docs.</span> </div></div><div class="field"> <label class="field__label" for="f-url">Website</label> <label class="input-group"> <input class="input" id="f-url" type="url" name="url" inputmode="url" required placeholder="zazz.design" autocomplete="url" autocapitalize="off" spellcheck="false" enterkeyhint="go" aria-describedby="f-url-hint" /> <span class="input-group__addon"> <span class="input-group__text">https://</span> </span> <span class="input-group__addon" data-align="inline-end"> <button class="button" data-variant="primary" data-size="sm" type="submit">Apply</button> </span> </label> <div class="field__description"> <span class="field__hint" id="f-url-hint">Where people can find you online.</span> <span class="field__error" role="alert">Enter a valid URL, e.g. zazz.design.</span> </div></div><div class="field"> <label class="field__label" for="f-prompt">Prompt</label> <label class="input-group"> <textarea id="f-prompt" class="textarea" name="prompt" inputmode="text" placeholder="Ask anything…" aria-describedby="f-prompt-hint" ></textarea> <span class="input-group__addon" data-align="block-end"> <button class="button bg-background ml-auto" data-size="sm" type="submit"> <span>Send</span> <kbd>↵</kbd> </button> </span> </label> <div class="field__description"> <span class="field__hint" id="f-prompt-hint" >Enter to send · Shift + Enter for a new line.</span > </div></div>Password group
The password-group example wraps the field in <input-password>, which adds the show/hide
toggle behavior and exposes the component script in the JS tab.
Loading components…
<div class="field"> <label class="field__label" for="f-login-password">Password</label> <input-password> <label class="password-group"> <input class="input" id="f-login-password" name="password" type="password" autocomplete="new-password" minlength="8" required placeholder="••••••••" aria-describedby="f-login-password-hint f-login-password-warning" /> <span class="password-group__addon" data-align="inline-end"> <button class="button password-group__toggle" id="f-login-password-toggle" data-variant="ghost" data-size="icon-sm" type="button" aria-pressed="false" aria-label="Show password" aria-describedby="f-login-password-warning" > <svg class="password-group__icon password-group__icon--show" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <path d="M128,56C48,56,16,128,16,128s32,72,112,72,112-72,112-72S208,56,128,56Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <circle cx="128" cy="128" r="40" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> <svg class="password-group__icon password-group__icon--hide" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" aria-hidden="true" > <rect width="256" height="256" fill="none" /> <line x1="48" y1="40" x2="208" y2="216" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <path d="M154.9,157.6A40,40,0,0,1,101,98.4" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <path d="M73.8,69.7C33.6,90.6,16,128,16,128s32,72,112,72a118.1,118.1,0,0,0,54.1-12.8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> <path d="M208.6,169.1C229.8,149.1,240,128,240,128S208,56,128,56a126,126,0,0,0-20.5,1.6" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16" /> </svg> </button> </span> </label> </input-password> <div class="field__description"> <span class="field__hint" id="f-login-password-hint">Use eight or more characters.</span> <span class="field__error" role="alert">Password must be at least 8 characters.</span> </div> <span class="sr-only" id="f-login-password-warning" >Warning: showing the password makes it visible to anyone near your screen.</span ></div>// password.js"use strict";/** * @fileoverview `<input-password>` — HTML web component for password visibility. * @description Light-DOM custom element that adds show/hide behavior to a * standard password field. Wrap the existing `.password-group` markup — the * element finds the input and the `.password-group__toggle` button, flips the * input between `type="password"` and `type="text"` on click, and keeps * `aria-pressed` and `aria-label` in sync. The icon swap is pure CSS, driven * by `aria-pressed` (see _password-group.css). * * Without JavaScript the field degrades to a regular password input; the * toggle button simply does nothing. * * Configuration (attributes on `<input-password>`): * - `label-show`: Toggle label while the password is hidden (default "Show password"). * - `label-hide`: Toggle label while the password is visible (default "Hide password"). * * @example * <input-password> * <label class="password-group"> * <input class="input" type="password" autocomplete="current-password" /> * <span class="password-group__addon" data-align="inline-end"> * <button class="button password-group__toggle" type="button" * aria-pressed="false" aria-label="Show password">…</button> * </span> * </label> * </input-password> */class InputPassword extends HTMLElement { /** @type {AbortController|null} */ #controller = null; connectedCallback() { if (this.#controller) return; const input = this.querySelector('input[type="password"], input[type="text"]'); const toggle = this.querySelector(".password-group__toggle"); if (!(input instanceof HTMLInputElement) || !(toggle instanceof HTMLElement)) return; this.#controller = new AbortController(); toggle.addEventListener( "click", () => { const reveal = input.type === "password"; input.type = reveal ? "text" : "password"; toggle.setAttribute("aria-pressed", String(reveal)); toggle.setAttribute( "aria-label", reveal ? this.getAttribute("label-hide") || "Hide password" : this.getAttribute("label-show") || "Show password", ); }, { signal: this.#controller.signal }, ); } disconnectedCallback() { this.#controller?.abort(); this.#controller = null; }}// Register the element (guarded against double script loads)if (typeof window !== "undefined" && !customElements.get("input-password")) { customElements.define("input-password", InputPassword);}// Attach to window for parity with the other component scripts, and export for// module consumers (loaded for its side effect — the custom-element registration).if (typeof window !== "undefined") { window.InputPassword = InputPassword;}export { InputPassword };API
| Attribute | Where | Values |
|---|---|---|
data-align | .input-group__addon | inline-start, inline-end, block-start, block-end |