Two factor and sign in
Security key and passkey
Prompt for a passkey or a hardware security key: an animated key mark, the steps, platform hints (Touch ID, Windows Hello, Android, USB or NFC keys) and error, retry and success states through classes. The page calls WebAuthn and switches the classes.
Example
<div class="grid gap-4 md:grid-cols-2 items-start">
<div class="auth-card">
<div class="auth-key">
<div class="auth-key-visual" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7.5 5.2A8 8 0 0 1 20 12c0 1.4-.1 2.7-.4 4"/><path d="M4.3 9A8 8 0 0 1 5.5 7"/><path d="M4 12.5c0 1.6-.3 3-.9 4.2"/><path d="M8 10.5a4 4 0 0 1 8 1.5c0 2.8-.4 5.3-1.3 7.5"/><path d="M12 12c0 3.2-.6 6-1.9 8.3"/><path d="M8.2 14.5c-.2 1.9-.8 3.6-1.7 5"/></svg></div>
<header class="auth-header">
<h2 class="auth-title">Use your passkey</h2>
<p class="auth-description">Your browser will ask you to confirm it's you.</p>
</header>
<ol class="auth-key-steps">
<li>Choose the passkey for you@example.com when your browser asks.</li>
<li>Confirm with your fingerprint, face, screen lock or security key.</li>
</ol>
<ul class="auth-key-hints" aria-label="Works with">
<li class="auth-key-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 11V5.5a1.5 1.5 0 0 1 3 0V10l4.8 1a2 2 0 0 1 1.6 2.3l-.8 4.5a3 3 0 0 1-3 2.5h-3.3a3 3 0 0 1-2.4-1.2L5.5 15.5a1.5 1.5 0 0 1 2.3-1.9L9 15"/></svg> Touch ID</li>
<li class="auth-key-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 8V6a2 2 0 0 1 2-2h2M16 4h2a2 2 0 0 1 2 2v2M20 16v2a2 2 0 0 1-2 2h-2M8 20H6a2 2 0 0 1-2-2v-2"/><path d="M9 9.5v1M15 9.5v1M12 9.5v3.5h-1M9.5 16a3.5 3.5 0 0 0 5 0"/></svg> Windows Hello</li>
<li class="auth-key-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="7" y="2.5" width="10" height="19" rx="2.5"/><path d="M11 18.5h2"/></svg> Android</li>
<li class="auth-key-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 8.5a5 5 0 0 1 0 7M9.5 6a8.5 8.5 0 0 1 0 12M13 3.5a12 12 0 0 1 0 17"/></svg> USB or NFC key</li>
</ul>
<p class="auth-key-message" role="status"></p>
</div>
<div class="auth-alt"><a class="link link-muted" href="#methods">Use another method</a></div>
</div>
<div class="auth-card">
<div class="auth-key auth-key-error">
<div class="auth-key-visual" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="2.5" width="6" height="6" rx="1"/><rect x="7" y="8.5" width="10" height="13" rx="3"/><circle cx="12" cy="15" r="1.75"/></svg></div>
<header class="auth-header">
<h2 class="auth-title">We couldn't verify your key</h2>
</header>
<p class="auth-key-message" role="status">The request timed out or was cancelled. Insert your key and try again.</p>
<div class="flex flex-wrap justify-center gap-2">
<button type="button" class="btn btn-primary"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 11.5A8 8 0 0 0 5.6 6.8L4 8.5"/><path d="M4 4v4.5h4.5"/><path d="M4 12.5a8 8 0 0 0 14.4 4.7L20 15.5"/><path d="M20 20v-4.5h-4.5"/></svg> Retry</button>
<button type="button" class="btn btn-ghost">Use another method</button>
</div>
</div>
</div>
</div>JavaScript API
Notes
| Note | Description |
|---|---|
UI only: call navigator.credentials.get({ publicKey }) (or create() to register) when the screen opens, with the challenge and allowCredentials from your server. | |
Add .auth-key-error to the .auth-key element when the promise rejects (NotAllowedError means cancelled or timed out) and show the Retry button; add .auth-key-success when your server accepts the assertion. | |
Put the message in .auth-key-message (role="status"), it is shown only in the error and success states. |
Classes
8 classes. Click a name to copy it. Utilities on the same element always win over component defaults, so any of them can be adjusted with plain utility classes.
| Class | Type | What it does |
|---|---|---|
| Base | Base class of the security key and passkey. On a <div> in the example. Sets grid display, alignment, gap and text style, and --tone.CSS |
|
| Part | Part of the security key and passkey. On a <div> in the example. Sets grid display, alignment, width, height, text color and background.CSS |
|
| Part | Part of the security key and passkey. On an <ol> in the example. Sets grid display, gap, width, margin, padding and text style.CSS |
|
| Part | Part of the security key and passkey. On an <ul> in the example. Sets flex display, alignment, gap, margin and padding.CSS |
|
| Part | Part of the security key and passkey. On a <li> in the example. Sets inline-flex display, alignment, gap, height, padding and text size.CSS |
|
| Part | Part of the security key and passkey. On a <p> in the example. Sets hidden by default, margin, text size and text style.CSS |
|
| Modifier | Modifier, combined with the auth-key class. On a <div> in the example. Sets hidden by default, animation, block display and text color, and --tone.CSS |
|
| Tone | Success tone. Not used in the example above. Sets hidden by default, animation, block display and text color, and --tone.CSS |
Usage
Components ship inside the same stylesheet as the utilities. There is nothing else to install and no script to load: link Monsoon and write the markup.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/monsooncss">
<!-- Security key and passkey: start from the base class, then add modifiers -->
<div class="grid gap-4 md:grid-cols-2 items-start">
<div class="auth-card">
<div class="auth-key">
<div class="auth-key-visual" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7.5 5.2A8 8 0 0 1 20 12c0 1.4-.1 2.7-.4 4"/><path d="M4.3 9A8 8 0 0 1 5.5 7"/><path d="M4 12.5c0 1.6-.3 3-.9 4.2"/><path d="M8 10.5a4 4 0 0 1 8 1.5c0 2.8-.4 5.3-1.3 7.5"/><path d="M12 12c0 3.2-.6 6-1.9 8.3"/><path d="M8.2 14.5c-.2 1.9-.8 3.6-1.7 5"/></svg></div>
<header class="auth-header">
<h2 class="auth-title">Use your passkey</h2>
<p class="auth-description">Your browser will ask you to confirm it's you.</p>
</header>
<ol class="auth-key-steps">
<li>Choose the passkey for you@example.com when your browser asks.</li>
<li>Confirm with your fingerprint, face, screen lock or security key.</li>
</ol>
<ul class="auth-key-hints" aria-label="Works with">
<li class="auth-key-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 11V5.5a1.5 1.5 0 0 1 3 0V10l4.8 1a2 2 0 0 1 1.6 2.3l-.8 4.5a3 3 0 0 1-3 2.5h-3.3a3 3 0 0 1-2.4-1.2L5.5 15.5a1.5 1.5 0 0 1 2.3-1.9L9 15"/></svg> Touch ID</li>
<!-- the full example is in the Code tab above -->Theme it with --seed on any ancestor, for example style="--seed: #0d9488", and force a scheme with class="dark" or class="light" on a wrapper.
Accessibility
- Use
buttonfor actions andafor navigation. Icon only buttons need anaria-label; decorative icons carryaria-hidden="true". - Announces itself with
role="status"(polite) orrole="alert"(assertive). Use alert only for messages that need immediate attention. - Every interactive part shows a visible focus ring with
:focus-visible, and tone colors pair with-foregroundtokens that keep text at 4.5:1 or better in light and dark mode.