Password toggle
A password field in an input group with a
.re-input-group__action button marked data-re-password-toggle. Opt into the
behavior:
import { enhancePasswordToggle } from "@relements/core";enhancePasswordToggle(document);The button points at its field via aria-controls (or just shares the input
group). Toggling flips input.type, reflects aria-pressed, sets
data-revealed for icon swapping, and preserves focus and caret position. The
accessible name stays "Show password" — a toggle button conveys on/off via
aria-pressed, not by renaming itself.
Because the button only works with JavaScript (a password field has no native
reveal), author it hidden; the enhancer un-hides it. So with no JavaScript the
field stays a normal, fully usable password input and no dead control is shown.
The button holds two icons marked data-when="hidden" and data-when="shown";
the behavior toggles their hidden attribute.
<label class="re-field">
<span class="re-field__label">Password</span>
<span class="re-input-group">
<input
class="re-input"
type="password"
id="pw"
value="hunter2"
autocomplete="current-password"
/>
<button
class="re-input-group__action"
type="button"
data-re-password-toggle
aria-controls="pw"
aria-label="Show password"
hidden
>
<svg
data-when="hidden"
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
stroke-width="1.6"
aria-hidden="true"
>
<path d="M1.5 10S4.5 4 10 4s8.5 6 8.5 6-3 6-8.5 6-8.5-6-8.5-6Z" />
<circle cx="10" cy="10" r="2.5" />
</svg>
<svg
data-when="shown"
hidden
viewBox="0 0 20 20"
fill="none"
stroke="currentColor"
stroke-width="1.6"
aria-hidden="true"
>
<path d="M1.5 10S4.5 4 10 4s8.5 6 8.5 6-3 6-8.5 6-8.5-6-8.5-6Z" />
<circle cx="10" cy="10" r="2.5" />
<path d="m3 3 14 14" />
</svg>
</button>
</span>
</label> Accessibility
Section titled “Accessibility”- Keyboard — the toggle is a native
<button type="button">: Tab moves to it, Enter or Space activates it. The field stays a native password<input>with all its native behavior. Activating preserves focus and caret position so typing isn’t interrupted by the reveal. - Focus — both controls show the standard
:focus-visiblering. The input group owns the outer ring via:focus-within; the action button gets an inset ring instead, so it stays inside the group’s clipped (overflow-hidden) rounded box. - Semantics —
aria-controlsties the button to the input it reveals. State is conveyed byaria-pressed(falsehidden,trueshown) on a button whose accessible name stays"Show password"— assistive tech announces the pressed state rather than a renamed control. The two eye / eye-off<svg>icons are decorative (aria-hidden="true"); the behavior swaps them and never exposes them to the accessibility tree. - Notes — because a password field has no native reveal, the button is a
JavaScript-only affordance authored
hiddenand un-hidden by the enhancer, so no JavaScript means no dead, announced control. See the accessibility guide.