Show password strength on the signup form
Angular · Angular · beginner · greenfield
Adds the signup password meter as a standalone component on signals. The score and the unmet-rules list are both computed from the same rule table, so the label and the checklist can never disagree. The strength line doubles as a persistent status live region, and empty input shows no feedback text, per the spec.
Requirements
- Build the standalone `PasswordStrengthComponent` for the signup form: a labelled password input plus live feedback below it.
- Four rules, each worth one point: length of at least 12 (measured in UTF-16 code units, i.e. JavaScript's `length`); both an upper- and a lowercase ASCII letter; at least one digit; at least one character that is not an ASCII letter or digit.
- Strength label from the score: 0–1 points `Weak`, 2–3 `Fair`, 4 `Strong`.
- While the password is empty, show no label and no rule list at all.
- Below the label, list only the rules that are **not yet** satisfied, so the user sees what's left; the list disappears when all four pass. Rule wording must state the ASCII character classes it actually checks.
- Feedback must be plain visible text (no colour-only signalling), and the strength label must be announced to assistive tech as it changes: a **persistent** polite live region (`role="status"` on an element that stays in the DOM), referenced from the input via `aria-describedby`.
Files touched
- src/app/signup/password-strength.component.ts
--- src/app/signup/password-strength.component.ts
+import { Component, computed, signal } from '@angular/core';
+
+interface StrengthRule {
+ id: string;
+ label: string;
+ test: (password: string) => boolean;
+}
+
+const RULES: StrengthRule[] = [
+ { id: 'length', label: 'At least 12 characters', test: (p) => p.length >= 12 },
+ { id: 'case', label: 'Upper and lower case letters (a–z and A–Z)', test: (p) => /[a-z]/.test(p) && /[A-Z]/.test(p) },
+ { id: 'digit', label: 'At least one digit (0–9)', test: (p) => /[0-9]/.test(p) },
+ { id: 'symbol', label: 'At least one symbol (any character that is not an ASCII letter or digit)', test: (p) => /[^A-Za-z0-9]/.test(p) },
+];
+
+@Component({
+ selector: 'app-password-strength',
+ standalone: true,
+ template: `
+ <label for="new-password">New password</label>
+ <input
+ id="new-password"
+ type="password"
+ autocomplete="new-password"
+ aria-describedby="password-strength-status"
+ [value]="password()"
+ (input)="onInput($event)"
+ />
+ <p id="password-strength-status" class="strength" role="status">