Password Strength
Under the password field, a bar fills a third, two thirds or all the way, with a Weak, Medium or Strong verdict recomputed on every key.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Forms has 20 effects, including 2 free. Explore the category →
3 usage examples



How it works
On every input event, the password earns one point per criterion met: 6 characters or more, 10 or more, an uppercase letter (A-Z), a digit, a character that is neither a letter nor a digit (accents included). 0 to 2 points: bar at 33% in red and "Weak"; 3 points: 66% in orange and "Medium"; 4 or 5: full green bar and "Strong". Empty field: empty bar and the starting text, "Password strength".
The state lives in a single attribute, data-strength on .pwsm-group (empty, weak, medium, strong); the CSS derives width and colours from it and the bar slides in 0.4 s. No inline style is written on the bar: clearing the field and typing again makes it start over normally.
Labels are read from the HTML (hidden .pwsm-words block), so the same script works in French and English. The score checks no list of known passwords: "Password1!" gets "Strong". It is a hint that guides typing, not a server-side security check.
Accessibility
- Label and autofill: "Password" in a visually hidden
<label>,autocomplete="new-password"so password managers offer to generate one. - Verdict announced: the text under the bar is an
aria-live="polite"region rewritten only when the verdict changes; the field points to it througharia-describedby. - Not colour alone: each level has its own bar length and word; the bar itself is
aria-hidden. - Reduced motion: under
prefers-reduced-motion: reduce, the bar changes length without sliding. - Limit: the score does not say which criterion is missing. Keep a visible sentence listing the rules (as in the bank example).
Browser compatibility
2 KB of JavaScript, CSS with variables and attribute selectors. Measured on 08/11/2026 in Chromium, Firefox and WebKit (Playwright): full green bar after clearing the field and typing again, two independent copies, touch at 390 px, reduced motion, no errors. Floor set by NodeList.forEach (Chrome 51, Firefox 50, Safari 10, according to @mdn/browser-compat-data).
Without JavaScript the password field works normally; the bar stays empty and the "Password strength" text never changes.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0286-root">
<div class="pwsm-group" data-strength="empty">
<label class="pwsm-field">
<span class="pwsm-sr">Password</span>
<input class="pwsm-input" type="password" name="password" autocomplete="new-password" placeholder="Enter a password">
</label>
<div class="pwsm-meter" aria-hidden="true">
<div class="pwsm-fill"></div>
</div>
<div class="pwsm-label" aria-live="polite">Password strength</div>
<span class="pwsm-words" hidden>
<span data-level="empty">Password strength</span>
<span data-level="weak">Weak</span>
<span data-level="medium">Medium</span>
<span data-level="strong">Strong</span>
</span>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Level colours (CSS — --pwsm-weak, --pwsm-medium, --pwsm-strong on .fx-0286-root) |
#ef4444 · #f59e0b · #10b981 | On white, darken them (#dc2626, #d97706, #059669) to keep the text readable. |
Bar (CSS — --pwsm-bar-height, --pwsm-track) |
4px · 10% white | Bar thickness and the colour of its empty track. |
Labels (HTML — .pwsm-words) |
Password strength · Weak · Medium · Strong | Translate or reword here ("Too short", "Fair", "Excellent"): the script reads these texts again on every change. |
Field (CSS — --pwsm-field, --pwsm-border, --pwsm-focus, --pwsm-text, --pwsm-muted) |
5% white · 10% white · #6366f1 · #fff · #8b8b93 | --pwsm-muted colours the text while the field is empty. |
Sizes (CSS — --pwsm-width, --pwsm-font-size, --pwsm-label-size) |
280px · 1rem · .75rem | Width is a maximum; 100% follows the container. Enlarge the verdict for a presentation screen. |
Demo frame (CSS — --pwsm-bg, --pwsm-min-height) |
#0a0a0f · 300px | In a real form: --pwsm-bg:transparent; --pwsm-min-height:0. |
Scoring (JS — score() function and thresholds in update()) |
1 point per criterion, Weak ≤ 2, Medium = 3, Strong ≥ 4 | To require 12 characters, replace pw.length >= 10 with pw.length >= 12. |
FAQ
form.addEventListener('submit', e => { if (group.dataset.strength !== 'strong') e.preventDefault(); }), where group is the field's .pwsm-group. Always check again on the server.score() and keep the effect's display.reset, yes, automatically. After a value set by script (a "Generate" button, a password manager that fires no event), call input.dispatchEvent(new Event('input', {bubbles: true})): the bar and the verdict are recalculated.