Component
Password Visibility Toggle
Password Visibility Toggle.
- Component
- Vanilla
- HTML
- MIT
Preview
Live component
No interactive preview for this component.
Install it with the CLI below, or read the implementation on this page.
Install
Add to your project
npx devsnips add Vanilla/Components/Other/password-visibility-toggle Vanilla/Components/Other/password-visibility-toggle <!--
Password Visibility Toggle Input
- Toggle password visibility
- Password strength indicator
- Accessible toggle button
- Real-time validation
- Mobile-friendly design
-->
<div class="password-container">
<div class="password-field">
<label for="password">Password</label>
<div class="input-wrapper">
<input
type="password"
id="password"
name="password"
required
minlength="8"
aria-describedby="password-requirements password-error"
autocomplete="new-password"
>
<button
type="button"
class="toggle-password"
aria-label="Show password"
aria-pressed="false"
>
<!-- Show password icon -->
<svg class="show-icon" viewBox="0 0 24 24" width="24" height="24">
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" fill="currentColor"/>
</svg>
<!-- Hide password icon -->
<svg class="hide-icon" viewBox="0 0 24 24" width="24" height="24">
<path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z" fill="currentColor"/>
</svg>
</button>
</div>
<div id="password-error" class="error-message" aria-live="polite"></div>
</div>
<div id="password-requirements" class="requirements">
<h3>Password must contain:</h3>
<ul>
<li data-requirement="length">
<svg viewBox="0 0 24 24" width="16" height="16">
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/>
</svg>
<span>At least 8 characters</span>
</li>
<li data-requirement="uppercase">
<svg viewBox="0 0 24 24" width="16" height="16">
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/>
</svg>
<span>One uppercase letter</span>
</li>
<li data-requirement="lowercase">
<svg viewBox="0 0 24 24" width="16" height="16">
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/>
</svg>
<span>One lowercase letter</span>
</li>
<li data-requirement="number">
<svg viewBox="0 0 24 24" width="16" height="16">
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/>
</svg>
<span>One number</span>
</li>
<li data-requirement="special">
<svg viewBox="0 0 24 24" width="16" height="16">
<circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/>
</svg>
<span>One special character</span>
</li>
</ul>
</div>
<div class="strength-meter" aria-label="Password strength">
<div class="strength-bar"></div>
</div>
</div>
<style>
.password-container {
max-width: 400px;
margin: 2rem auto;
padding: 0 1rem;
}
.password-field {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.25rem;
font-weight: 500;
color: var(--ds-foreground, #333);
}
.input-wrapper {
position: relative;
display: flex;
align-items: center;
}
input {
width: 100%;
padding: 0.75rem;
padding-right: 3rem;
border: 2px solid var(--ds-border, #ddd);
border-radius: var(--ds-radius-sm, 4px);
font-size: 1rem;
transition: all 0.3s;
}
.toggle-password {
position: absolute;
right: 0.75rem;
padding: 0.25rem;
border: none;
background: none;
color: var(--ds-muted, #666);
cursor: pointer;
transition: color 0.3s;
}
.toggle-password:hover {
color: var(--ds-foreground, #333);
}
.hide-icon {
display: none;
}
.toggle-password[aria-pressed="true"] .show-icon {
display: none;
}
.toggle-password[aria-pressed="true"] .hide-icon {
display: block;
}
.error-message {
margin-top: 0.25rem;
font-size: 0.875rem;
color: var(--ds-danger, #f44336);
min-height: 1.25rem;
}
.requirements {
margin: 1rem 0;
padding: 1rem;
background: var(--ds-surface-2, #f8f9fa);
border-radius: var(--ds-radius-sm, 4px);
}
.requirements h3 {
margin: 0 0 0.5rem;
font-size: 0.875rem;
color: var(--ds-muted, #666);
}
.requirements ul {
margin: 0;
padding: 0;
list-style: none;
}
.requirements li {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--ds-muted, #666);
font-size: 0.875rem;
margin-bottom: 0.25rem;
}
.requirements li[data-valid="true"] {
color: var(--ds-success, #4caf50);
}
.requirements li[data-valid="true"] svg {
stroke: none;
fill: var(--ds-success, #4caf50);
}
.strength-meter {
height: 4px;
background: var(--ds-border, #ddd);
border-radius: 2px;
overflow: hidden;
}
.strength-bar {
height: 100%;
width: 0;
background: var(--ds-danger, #f44336);
transition: all 0.3s;
}
.strength-bar[data-strength="weak"] {
width: 33.33%;
background: var(--ds-danger, #f44336);
}
.strength-bar[data-strength="medium"] {
width: 66.66%;
background: var(--ds-warning, #ffa726);
}
.strength-bar[data-strength="strong"] {
width: 100%;
background: var(--ds-success, #4caf50);
}
/* Focus styles */
input:focus {
outline: none;
border-color: var(--ds-accent, #4a90e2);
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.1);
}
.toggle-password:focus-visible {
outline: 2px solid var(--ds-accent, #4a90e2);
outline-offset: 2px;
border-radius: var(--ds-radius-sm, 4px);
}
/* Mobile optimization */
@media (max-width: 600px) {
input {
font-size: 16px; /* Prevent iOS zoom */
}
.toggle-password {
padding: 0.5rem;
margin: -0.5rem;
}
}
/* devsnips-qa:quality-bar */
@media (prefers-reduced-motion: reduce){*{animation-duration:0.01ms !important;animation-iteration-count:1 !important;transition-duration:0.01ms !important;scroll-behavior:auto !important;}}
/* DevSnips design tokens (Swiss) — edit tokens.css to re-theme */
:root{
--ds-bg:#fff;--ds-surface:#fff;--ds-surface-2:#f7f7f7;--ds-surface-3:#ededed;--ds-foreground:#0a0a0a;--ds-muted:#525252;--ds-subtle:#a3a3a3;--ds-border:#e5e5e5;--ds-border-strong:#d4d4d4;--ds-accent:#2563eb;--ds-accent-hover:#1d4ed8;--ds-accent-fg:#fff;--ds-accent-soft:#eff6ff;--ds-success:#16a34a;--ds-success-soft:#f0fdf4;--ds-warning:#d97706;--ds-warning-soft:#fffbeb;--ds-danger:#dc2626;--ds-danger-soft:#fef2f2;--ds-info:#2563eb;--ds-info-soft:#eff6ff;--ds-font-sans:system-ui,-apple-system,'Segoe UI',Roboto,Inter,'Helvetica Neue',Arial,sans-serif;--ds-font-mono:ui-monospace,'SF Mono','JetBrains Mono',Menlo,Consolas,monospace;--ds-text-xs:.75rem;--ds-text-sm:.875rem;--ds-text-base:1rem;--ds-text-lg:1.125rem;--ds-text-xl:1.25rem;--ds-text-2xl:1.5rem;--ds-text-3xl:1.875rem;--ds-text-4xl:2.25rem;--ds-leading-tight:1.25;--ds-leading-normal:1.5;--ds-leading-relaxed:1.625;--ds-weight-normal:400;--ds-weight-medium:500;--ds-weight-semibold:600;--ds-weight-bold:700;--ds-space-0:0;--ds-space-px:1px;--ds-space-1:.25rem;--ds-space-2:.5rem;--ds-space-3:.75rem;--ds-space-4:1rem;--ds-space-5:1.25rem;--ds-space-6:1.5rem;--ds-space-8:2rem;--ds-space-10:2.5rem;--ds-space-12:3rem;--ds-space-16:4rem;--ds-radius-sm:.25rem;--ds-radius-md:.5rem;--ds-radius-lg:.75rem;--ds-radius-xl:1rem;--ds-radius-2xl:1.5rem;--ds-radius-full:9999px;--ds-shadow-sm:0 1px 2px 0 rgba(0,0,0,.05);--ds-shadow-md:0 4px 6px -1px rgba(0,0,0,.08),0 2px 4px -2px rgba(0,0,0,.05);--ds-shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.05);--ds-duration-fast:120ms;--ds-duration-normal:200ms;--ds-duration-slow:300ms;--ds-ease-out:cubic-bezier(.16,1,.3,1);--ds-ease-in-out:cubic-bezier(.4,0,.2,1);--ds-ring:0 0 0 2px #2563eb;--ds-container:1200px;--ds-gutter:1rem;
}
@media (prefers-color-scheme: dark){:root{--ds-bg:#0a0a0a;--ds-surface:#111;--ds-surface-2:#171717;--ds-surface-3:#1f1f1f;--ds-foreground:#fafafa;--ds-muted:#a3a3a3;--ds-subtle:#737373;--ds-border:#262626;--ds-border-strong:#404040;--ds-accent:#3b82f6;--ds-accent-hover:#60a5fa;--ds-accent-fg:#fff;--ds-accent-soft:#172554;--ds-success:#22c55e;--ds-success-soft:#052e16;--ds-warning:#f59e0b;--ds-warning-soft:#422006;--ds-danger:#ef4444;--ds-danger-soft:#450a0a;--ds-info:#3b82f6;--ds-info-soft:#172554;--ds-ring:0 0 0 2px #3b82f6;}}
</style>
<script>
const passwordInput = document.querySelector('#password');
const toggleButton = document.querySelector('.toggle-password');
const requirements = document.querySelectorAll('.requirements li');
const strengthBar = document.querySelector('.strength-bar');
const errorMessage = document.querySelector('#password-error');
// Password toggle
toggleButton.addEventListener('click', () => {
const isVisible = passwordInput.type === 'text';
passwordInput.type = isVisible ? 'password' : 'text';
toggleButton.setAttribute('aria-pressed', !isVisible);
toggleButton.setAttribute('aria-label', isVisible ? 'Show password' : 'Hide password');
});
// Password validation
const patterns = {
length: password => password.length >= 8,
uppercase: password => /[A-Z]/.test(password),
lowercase: password => /[a-z]/.test(password),
number: password => /\d/.test(password),
special: password => /[!@#$%^&*]/.test(password)
};
function validatePassword(password) {
let valid = true;
let validCount = 0;
requirements.forEach(requirement => {
const type = requirement.dataset.requirement;
const isValid = patterns[type](password);
requirement.dataset.valid = isValid;
if (isValid) validCount++;
else valid = false;
});
// Update strength indicator
if (password.length === 0) {
strengthBar.removeAttribute('data-strength');
strengthBar.style.width = '0';
} else if (validCount <= 2) {
strengthBar.dataset.strength = 'weak';
} else if (validCount <= 4) {
strengthBar.dataset.strength = 'medium';
} else {
strengthBar.dataset.strength = 'strong';
}
// Update error message
if (password.length > 0 && !valid) {
errorMessage.textContent = 'Please meet all password requirements';
passwordInput.setAttribute('aria-invalid', 'true');
} else {
errorMessage.textContent = '';
passwordInput.setAttribute('aria-invalid', 'false');
}
return valid;
}
passwordInput.addEventListener('input', e => {
validatePassword(e.target.value);
});
// Handle paste events
passwordInput.addEventListener('paste', e => {
setTimeout(() => {
validatePassword(e.target.value);
}, 0);
});
// Clear validation on focus
passwordInput.addEventListener('focus', () => {
if (!passwordInput.value) {
requirements.forEach(requirement => {
requirement.removeAttribute('data-valid');
});
strengthBar.removeAttribute('data-strength');
}
});
</script> # Snippet 15 Password Visibility Toggle
Snippet 15 Password Visibility Toggle with responsive design with animations and hover interactions.
**Technology:** vanilla
**Category:** components
**Subcategory:** other
**Tags:** login, animation, hover-effect, shadow, responsive, flexbox, center, layout
**Responsive:** Yes 334 lines UTF-8 · LF · Spaces: 2
Continue browsing