Last active
March 13, 2020 14:17
-
-
Save fstorr/0520fbdcbcfdf0284647b009eb323047 to your computer and use it in GitHub Desktop.
More accessible password-strength meter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<meta | |
name="viewport" | |
content="width=device-width, initial-scale=1, shrink-to-fit=no" | |
/> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.2.0/zxcvbn.js"></script> | |
<title>More-Accessible Password Strength Meter</title> | |
<style> | |
:root { | |
font-size: 100%; | |
box-sizing: border-box; | |
scroll-behavior: smooth; | |
-moz-osx-font-smoothing: grayscale; | |
-webkit-font-smoothing: antialiased; | |
} | |
*, | |
*::after, | |
*::before { | |
box-sizing: inherit; | |
} | |
body { | |
background: hsl(0,0%,100%); | |
color: hsl(0,0%,0%); | |
font: 1rem/1.5 sans-serif; /* line height = 24px */ | |
} | |
h1, | |
h2, | |
h3, | |
h4, | |
h5, | |
h6 { | |
font-weight: 500; | |
} | |
.hidden-visually { | |
clip: rect(0, 0, 0, 0); | |
-webkit-clip-path: inset(50%); | |
clip-path: inset(50%); | |
height: 1px; | |
overflow: hidden; | |
position: absolute; | |
white-space: nowrap; | |
width: 1px; | |
} | |
.pwd-wrapper { | |
display: flex; | |
flex-direction: column; | |
width: 22.06rem; | |
} | |
label > span { | |
display: block; | |
} | |
.pwd-wrapper input { | |
font: inherit; | |
margin: 0; | |
width: 100%; | |
} | |
div[role="meter"] { | |
background: hsl(141, 16%, 83%); | |
border-bottom: 1px solid hsl(0, 0%, 75%); | |
height: 1.125rem; | |
} | |
.meter-fill { | |
display: block; | |
} | |
@media (prefers-reduced-motion: no-preference) { | |
.meter-fill { | |
transition: width ease-in 0.3s; | |
} | |
} | |
.status-text { | |
font-size: 0.79rem; | |
} | |
</style> | |
</head> | |
<body> | |
<main> | |
<h1>More accessible password-strength meter</h1> | |
<p> | |
Based on the | |
<a href="https://css-tricks.com/password-strength-meter/" | |
>CSS Tricks Password Strength meter article</a | |
>. | |
</p> | |
<ul> | |
<li> | |
Ended up using aria instead of the | |
<code class="language-markup"><meter></code> element. | |
</li> | |
<li> | |
Used <code class="language-markup">aria-valuetext</code> to announce a | |
more user-friendly value to the user. | |
</li> | |
<li> | |
Used a visually-hidden | |
<code class="language-markup"><output></code> element to | |
announce the current value to screen reader users. | |
</li> | |
<li>Ensured color contrast meets minimum accessibility standards.</li> | |
<li> | |
Animation for meter strength supports | |
<code class="language-css">prefers-reduced-motion</code> | |
</li> | |
</ul> | |
<div class="pwd-wrapper"> | |
<label> | |
<span>Enter a password</span> | |
<input aria-describedby="aria-meter" id="new-pwd" type="password" /> | |
</label> | |
<div | |
aria-valuemax="100" | |
aria-valuemin="0" | |
aria-valuetext="Password strength: no password entered" | |
aria-valuenow="0" | |
id="aria-meter" | |
role="meter" | |
> | |
<svg width="0" height="1.125em" class="meter-fill" aria-hidden="true"> | |
<rect | |
fill="currentColor" | |
height="100%" | |
width="100%" | |
x="0" | |
y="0" | |
></rect> | |
<text x="4" y="13" fill="currentColor" class="status-text"></text> | |
</svg> | |
</div> | |
<output class="hidden-visually" for="aria-meter"></output> | |
</div> | |
</main> | |
<script> | |
document.addEventListener("DOMContentLoaded", function(e) { | |
const strength = { | |
0: { | |
msg: "Very Weak", | |
bar: "hsl(0,100%,44%)", | |
txt: "hsl(0,100%,100%)" | |
}, | |
1: { | |
msg: "Weak", | |
bar: "hsl(25,100%,50%)", | |
txt: "hsl(30,100%,0%)" | |
}, | |
2: { | |
msg: "Medium", | |
bar: "hsl(52,100%,50%)", | |
txt: "hsl(45,100%,0%)" | |
}, | |
3: { | |
msg: "Strong", | |
bar: "hsl(70,100%,50%)", | |
txt: "hsl(70,100%,0%)" | |
}, | |
4: { | |
msg: "Very Strong", | |
bar: "hsl(120,100%,22%)", | |
txt: "hsl(100,100%,100%)" | |
} | |
}; | |
const password = document.getElementById("new-pwd"); | |
const meter = document.querySelector("[role=meter]"); | |
const output = document.querySelector("output"); | |
const svg = document.querySelector("svg"); | |
const svgText = svg.querySelector("text"); | |
password.addEventListener("input", function() { | |
let val = password.value; | |
let result = zxcvbn(val); | |
// Update the password strength meter | |
meter.setAttribute("aria-valuenow", 20 * result.score); | |
// Update the text indicator | |
if (val !== "") { | |
svg.classList.remove("hidden-visually"); | |
meter.setAttribute( | |
"aria-valuetext", | |
`Password strength: ${strength[result.score].msg}` | |
); | |
svg.setAttribute("width", `${20 * (result.score + 1)}%`); | |
svg.style.color = strength[result.score].bar; | |
svgText.style.color = strength[result.score].txt; | |
svgText.textContent = strength[result.score].msg; | |
output.textContent = ""; | |
setTimeout(function() { | |
output.textContent = `password strength: ${ | |
strength[result.score].msg | |
}`; | |
}, 200); | |
} else { | |
meter.setAttribute( | |
"aria-valuetext", | |
"Password strength: no password entered" | |
); | |
svg.setAttribute("width", `0%`); | |
output.textContent = "Password strength: no password entered"; | |
} | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment