ExklusivBeta
Password Field <mv-password-field>
Passwortfeld, das dein natives <input type="password"> behält (damit Autofill, Passwortmanager und Formularversand einfach funktionieren) und einen echten Anzeigen/Verbergen-Button ergänzt, der die Cursorposition behält und beim Absenden wieder verbirgt, eine Caps-Lock-Warnung, solange das Feld fokussiert ist, und für neue Passwörter eine Checkliste der von dir deklarierten Regeln plus eine Stärkeeinschätzung in Worten, die Länge belohnt und gängige Wörter, Jahreszahlen, Folgen und Wiederholungen durchschaut. Was andere übersehen: Die Regeln stehen schon vor dem ersten Tastendruck da, damit niemand überraschend scheitert, der Fortschritt wird angesagt, sobald der Nutzer innehält (nie pro Zeichen), die Regeln steuern die native Validität des Inputs, sodass das Formular das Absenden verweigert, eine Match-Option bestätigt ein zweites Feld, das passende autocomplete (new-password oder current-password) wird für dich gesetzt, und ein angezeigtes Passwort wird von Cloud-Rechtschreibprüfungen ferngehalten.
| Kategorie | Formulare |
|---|---|
| Typ | Web Component (<mv-password-field>) |
| Status | Beta |
| Keywords | exclusive, light, password, form, validation, strength, caps-lock, show-password, sign-up, autocomplete, a11y |
When to use
- A sign-up, password reset or change-password form needs visible rules and a strength estimate
- A sign-in form needs a show/hide button and a Caps Lock warning without breaking password managers
- A new password must be typed twice and the form should block submission until both match
- The site has its own password policy, such as a minimum length or a ban on the user's name
Avoid when
- A complete log in, sign up, forgot password and code flow is needed as one screen → use Auth instead
- The secret is a short numeric one-time code sent by SMS or email → use OTP instead
- The value is an API key or token that is shown once and copied, not typed → use Reveal Once instead
Installation
node scripts/add.mjs password-field --out ./src/marvelousKI-Agent mit dem MCP-Server von Marvelous UI: install_components({ slugs: ["password-field"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Kopierte Dateien (inklusive Abhängigkeiten): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, components/password-field/password-field.js, components/password-field/password-field.css.
Verwendung
Schnellstart mit dem kleinsten funktionierenden Markup:
<mv-password-field purpose="new"><input name="password" aria-label="Password" required></mv-password-field>Referenz-Markup zum Starten und Anpassen mit Attributen, data-* und CSS-Variablen:
<div id="mv-pw-demo" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,19rem),1fr));gap:1.25rem;width:100%;max-width:880px;align-items:start">
<form class="mv-pw-demo-card" id="mv-pw-demo-signup" style="display:grid;gap:1rem;padding:1.5rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);background:var(--mv-surface);box-shadow:var(--mv-shadow-sm)">
<div>
<h3 style="margin:0;font-size:var(--mv-text-lg)">Create your account</h3>
<p style="margin:.25rem 0 0;color:var(--mv-fg-muted);font-size:var(--mv-text-sm)">Start your 14-day trial of Lumen Studio. No card needed.</p>
</div>
<div class="mv-field">
<label class="mv-label" for="mv-pw-demo-email">Work email</label>
<input class="mv-input" id="mv-pw-demo-email" type="email" name="email" value="[email protected]" autocomplete="username" required>
</div>
<div class="mv-field">
<label class="mv-label" for="mv-pw-demo-new">Password</label>
<mv-password-field purpose="new" rules="length:12 upper lower digit symbol">
<input id="mv-pw-demo-new" name="password" required>
</mv-password-field>
</div>
<div class="mv-field">
<label class="mv-label" for="mv-pw-demo-confirm">Confirm password</label>
<mv-password-field match="#mv-pw-demo-new">
<input id="mv-pw-demo-confirm" name="password-confirm" required>
</mv-password-field>
</div>
<button class="mv-button" type="submit">Create account</button>
<p id="mv-pw-demo-done" hidden style="margin:0;color:var(--mv-fg-muted);font-size:var(--mv-text-sm)"></p>
</form>
<form class="mv-pw-demo-card" style="display:grid;gap:1rem;padding:1.5rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-xl);background:var(--mv-surface);box-shadow:var(--mv-shadow-sm)" onsubmit="event.preventDefault()">
<div>
<h3 style="margin:0;font-size:var(--mv-text-lg)">Welcome back</h3>
<p style="margin:.25rem 0 0;color:var(--mv-fg-muted);font-size:var(--mv-text-sm)">Sign in to continue to your workspace.</p>
</div>
<div class="mv-field">
<label class="mv-label" for="mv-pw-demo-user">Email</label>
<input class="mv-input" id="mv-pw-demo-user" type="email" name="email" value="[email protected]" autocomplete="username">
</div>
<div class="mv-field">
<div class="mv-field-header">
<label class="mv-label" for="mv-pw-demo-current">Password</label>
<a href="#" style="font-size:var(--mv-text-xs);color:var(--mv-fg-muted)">Forgot password?</a>
</div>
<mv-password-field>
<input id="mv-pw-demo-current" name="password" value="Kyoto-in-autumn-88" required>
</mv-password-field>
</div>
<button class="mv-button" data-variant="outline" type="submit">Sign in</button>
</form>
</div>
<script type="module">
const signup = document.getElementById("mv-pw-demo-signup");
const field = signup.querySelector("mv-password-field[purpose]");
const email = signup.querySelector("#mv-pw-demo-email");
// A custom rule on top of the declared ones: the password must not contain the email's name.
field.customRules = [{
id: "no-name",
label: "Doesn’t include your name",
test: (value) => {
const name = email.value.split("@")[0].split(/[._-]/)[0].toLowerCase();
return (name.length < 3 || !value.toLowerCase().includes(name));
},
}];
signup.addEventListener("submit", (e) => {
e.preventDefault();
const done = document.getElementById("mv-pw-demo-done");
done.hidden = false;
done.textContent = `Account created for ${email.value}.`;
});
</script>API
Attributes
| Name | Typ | Default | Description |
|---|---|---|---|
purpose | new | current | current | new: sign-up, reset or change (autocomplete="new-password", checklist and strength). current: sign-in (autocomplete="current-password", reveal and Caps Lock only). Without it, an input already marked autocomplete="new-password" counts as new. |
rules | string | length:12 | Space-separated rules shown as a checklist when purpose="new": length:N (counted in visible characters, emoji included), upper, lower, digit, symbol (any character that is not a letter or digit, spaces included). Set rules="" to show the strength estimate alone. |
match | CSS selector | Makes this a confirmation field: the input (or the mv-password-field) it points to must hold the same value. Shows “Passwords match” or “Passwords don’t match” once typing starts, and sets the validity. Implies new-password and no checklist. | |
label | string | Password | Accessible name of the input the component creates when you give it none (prefer your own <input> with a <label for>). |
name | string | password | Form name of the created input (ignored when you provide your own <input>). |
Properties
| Name | Typ | Description |
|---|---|---|
strings | object | Override any default text: label, show, caps, length ({ one, other } with {n}), upper, lower, digit, symbol, met / unmet ({rule}), strength ({level}), weak, fair, strong, hint, common, sequence, repeat, summary ({met}, {total}, {level}), invalid ({list}), match, mismatch. Numbers, plurals and lists follow the nearest lang (Intl). Can be set before the element is defined. |
customRules | Array<{ id, label, test(value) }> | Extra rules checked after the declared ones (e.g. “Doesn’t include your name”); test returns true when met; no rule counts as met while the field is empty. Can be set before the element is defined. |
input | HTMLInputElement | The native input (read-only). |
visible | boolean | Whether the password is currently shown (read-only; use reveal()). |
state | { valid, met, total, strength, match } | Current result: strength is "weak" | "fair" | "strong" | null, match is true/false or null without match (read-only). |
Methods
| Name | Description |
|---|---|
reveal(show = true) | Show or hide the password, keeping the caret and selection; returns the new visibility. |
estimate(value) | Named export: pure, server-safe strength estimate, returns null or { level, score (1-3), bits, tip }. |
Events
| Name | Description |
|---|---|
mv-change | On every edit; detail = state ({ valid, met, total, strength, match }). |
mv-toggle | Before showing or hiding; detail = { visible }. Cancelable (e.g. a kiosk policy that forbids revealing). Hiding on submit is not cancelable. |
Content structure
| Name | Description |
|---|---|
input | Your own <input> (recommended: keeps id, name, required, minlength and your <label for>). It is forced to type="password" and gets the right autocomplete. Without one, an input is created. |
CSS classes
| Name | Description |
|---|---|
mv-password-field-control / -input / -toggle | Bordered row, the native input, the show/hide button. |
mv-password-field-caps | Caps Lock warning (hidden while off or unfocused). |
mv-password-field-strength / -bars / -level / -tip | Strength row: three segments (aria-hidden), the level in words, a tip or hint. data-level="weak|fair|strong". |
mv-password-field-rules / -rule | Checklist; each rule has data-met="true|false". |
mv-password-field-match | Confirmation line; data-ok="true|false". |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-password-field-radius | var(--mv-radius-md) | Corner radius of the field. |
--mv-password-field-weak | var(--mv-danger) | Segment color for a weak password. |
--mv-password-field-fair | var(--mv-warning) | Segment color for a fair password. |
--mv-password-field-strong | var(--mv-success) | Segment color for a strong password and met rules. |
Accessibility
The input stays a native <input type="password">, labelled by your <label for> (or by label when the component creates it). The reveal control is a real <button type="button"> named “Show password” whose state is aria-pressed (the name never changes, only the state and the eye icon); a pointer click keeps focus and caret in the field, the keyboard reaches it with Tab and Space or Enter. The requirement list is linked with aria-describedby, so it is read on focus before any typing, and each rule carries a hidden “Met: …” or “Not met: …” text instead of relying on the icon or color. Progress is announced by a polite status region once the user pauses for a second (“3 of 5 requirements met. Strength: Fair.”), only while the field has focus and never twice in a row; nothing is spoken per keystroke. Caps Lock (read from the key and pointer events while focused) shows a warning that is added to the description and announced once. Strength is written in words next to the segments, which are aria-hidden. The rules and the match set the input's custom validity, so the browser blocks submission and names what is missing; unmet rules only turn red after the browser flags the field (:user-invalid), not while typing. Edge's built-in reveal is hidden to avoid two eye buttons; password manager icons are left alone. The revealed text has spellcheck, autocorrect and autocapitalize off. Forced colors: bordered segments filled with CanvasText, visible focus outline. Transitions are color-only and switch off with reduced motion. Without JS the native field still works.