UI Components Easy
Password Field
Password input with show/hide toggle, strength indicator meter, and accessible label.
Open in Lab
MCP
css vanilla-js
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Inter, system-ui, sans-serif;
background: #050910;
color: #f2f6ff;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.demo {
width: 100%;
max-width: 420px;
}
.demo-title {
font-size: 1.5rem;
font-weight: 800;
margin-bottom: 0.375rem;
}
.demo-sub {
color: #475569;
font-size: 0.875rem;
margin-bottom: 2rem;
}
.section {
margin-bottom: 2rem;
}
.section-label {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #475569;
margin-bottom: 0.75rem;
}
/* ── Field ── */
.field-wrap {
display: flex;
flex-direction: column;
gap: 0.375rem;
}
.field-label {
font-size: 0.875rem;
font-weight: 500;
color: #cbd5e1;
}
.pw-input-wrap {
position: relative;
display: flex;
align-items: center;
}
.pw-input {
width: 100%;
height: 2.75rem;
padding: 0 3rem 0 0.875rem;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: #f2f6ff;
font-family: inherit;
font-size: 0.9375rem;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s;
}
.pw-input::placeholder {
color: #334155;
}
.pw-input:focus {
border-color: rgba(99, 179, 237, 0.5);
box-shadow: 0 0 0 3px rgba(99, 179, 237, 0.12);
}
/* ── Toggle button ── */
.pw-toggle {
position: absolute;
right: 0.625rem;
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
background: none;
border: none;
color: #475569;
cursor: pointer;
border-radius: 6px;
transition: color 0.15s, background 0.15s;
}
.pw-toggle:hover {
color: #94a3b8;
background: rgba(255, 255, 255, 0.06);
}
.pw-toggle:focus-visible {
outline: 2px solid rgba(99, 179, 237, 0.5);
outline-offset: 1px;
}
.icon {
width: 1.125rem;
height: 1.125rem;
display: block;
}
/* ── Strength meter ── */
.strength-meter {
display: flex;
align-items: center;
gap: 0.625rem;
margin-top: 0.5rem;
min-height: 1rem;
}
.strength-bars {
display: flex;
gap: 0.25rem;
flex: 1;
}
.strength-bar {
height: 4px;
flex: 1;
border-radius: 999px;
background: rgba(255, 255, 255, 0.08);
transition: background 0.3s;
}
/* level classes on .strength-meter */
.strength-meter[data-level="1"] .strength-bar:nth-child(1) {
background: #ef4444;
}
.strength-meter[data-level="2"] .strength-bar:nth-child(-n + 2) {
background: #f59e0b;
}
.strength-meter[data-level="3"] .strength-bar:nth-child(-n + 3) {
background: #3b82f6;
}
.strength-meter[data-level="4"] .strength-bars .strength-bar {
background: #22c55e;
}
.strength-label {
font-size: 0.72rem;
font-weight: 600;
white-space: nowrap;
color: #475569;
min-width: 3.5rem;
text-align: right;
}
.strength-meter[data-level="1"] .strength-label {
color: #ef4444;
}
.strength-meter[data-level="2"] .strength-label {
color: #f59e0b;
}
.strength-meter[data-level="3"] .strength-label {
color: #3b82f6;
}
.strength-meter[data-level="4"] .strength-label {
color: #22c55e;
}// ── Show / hide toggles ──
document.querySelectorAll(".pw-toggle").forEach(function (btn) {
var targetId = btn.dataset.target;
var input = document.getElementById(targetId);
var eyeOn = btn.querySelector(".icon-eye");
var eyeOff = btn.querySelector(".icon-eye-off");
btn.addEventListener("click", function () {
var isHidden = input.type === "password";
input.type = isHidden ? "text" : "password";
eyeOn.style.display = isHidden ? "none" : "";
eyeOff.style.display = isHidden ? "" : "none";
btn.setAttribute("aria-label", isHidden ? "Hide password" : "Show password");
});
});
// ── Strength meter ──
var LABELS = ["", "Weak", "Fair", "Good", "Strong"];
function score(pw) {
if (!pw) return 0;
var pts = 0;
if (pw.length >= 8) pts++;
if (/[a-z]/.test(pw)) pts++;
if (/[A-Z]/.test(pw)) pts++;
if (/[0-9]/.test(pw)) pts++;
if (/[^A-Za-z0-9]/.test(pw)) pts++;
// map 0-5 → 0-4 levels
if (pts === 0) return 0;
if (pts <= 2) return 1;
if (pts === 3) return 2;
if (pts === 4) return 3;
return 4;
}
document.querySelectorAll(".pw-input[data-strength]").forEach(function (input) {
var wrap = input.closest(".field-wrap");
var meter = wrap.querySelector(".strength-meter");
var label = wrap.querySelector(".strength-label");
input.addEventListener("input", function () {
var level = score(input.value);
if (level === 0) {
meter.removeAttribute("data-level");
label.textContent = "";
} else {
meter.setAttribute("data-level", level);
label.textContent = LABELS[level];
}
});
});<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Password Field</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<h1 class="demo-title">Password Field</h1>
<p class="demo-sub">Show/hide toggle with real-time strength indicator.</p>
<!-- Variant 1: Basic toggle -->
<section class="section">
<p class="section-label">Basic toggle</p>
<div class="field-wrap">
<label class="field-label" for="pw-basic">Password</label>
<div class="pw-input-wrap">
<input
id="pw-basic"
class="pw-input"
type="password"
placeholder="Enter password"
autocomplete="current-password"
/>
<button
class="pw-toggle"
type="button"
aria-label="Show password"
aria-controls="pw-basic"
data-target="pw-basic"
>
<svg class="icon icon-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
<svg class="icon icon-eye-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="display:none">
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/>
<path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/>
<line x1="1" y1="1" x2="23" y2="23"/>
</svg>
</button>
</div>
</div>
</section>
<!-- Variant 2: Strength meter -->
<section class="section">
<p class="section-label">With strength meter</p>
<div class="field-wrap">
<label class="field-label" for="pw-strength">New Password</label>
<div class="pw-input-wrap">
<input
id="pw-strength"
class="pw-input"
type="password"
placeholder="Create a strong password"
autocomplete="new-password"
data-strength
/>
<button
class="pw-toggle"
type="button"
aria-label="Show password"
aria-controls="pw-strength"
data-target="pw-strength"
>
<svg class="icon icon-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
<svg class="icon icon-eye-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="display:none">
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/>
<path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/>
<line x1="1" y1="1" x2="23" y2="23"/>
</svg>
</button>
</div>
<div class="strength-meter" aria-live="polite">
<div class="strength-bars">
<span class="strength-bar"></span>
<span class="strength-bar"></span>
<span class="strength-bar"></span>
<span class="strength-bar"></span>
</div>
<span class="strength-label"></span>
</div>
</div>
</section>
</div>
<script src="script.js"></script>
</body>
</html>Password Field
Password input field with visibility toggle and a real-time strength meter. Keyboard and screen-reader accessible.
Variants
| Variant | Description |
|---|---|
| Basic toggle | Show / hide password with eye icon button |
| Strength meter | Live 4-segment bar and label (Weak → Fair → Good → Strong) |
Strength scoring
Strength is computed from: length ≥ 8, contains uppercase, lowercase, digit, and special character. Each criterion adds one point (0–5), mapped to four levels.