Build a two-factor enrolment card with Tailwind CSS, max-w-xl on a soft neutral background. Above the fold: a small uppercase "Security" eyebrow, a headline, and a line naming a few authenticator apps. The pairing panel puts a square QR placeholder (a 5-column grid of small rounded squares inside a bordered tile) beside a column holding a "Can't scan it?" hint, the setup key in a monospaced pill with a Copy button, and a six-digit confirmation input with wide letter-spacing. Footer row: a muted Cancel link and a dark "Turn on" button. Below it, a hidden success panel with a green confirmation strip, a grid of one-time backup codes, and a "Copy all codes" button. Then add JavaScript that copies the setup key to the clipboard and flips the button label to "Copied" for a moment, strips non-digits from the confirmation field and keeps the "Turn on" button disabled until six digits are present, and on confirm swaps the pairing panel for the backup-code panel and wires its copy button to put every code on the clipboard as separate lines.
Paste it into Claude, Cursor, v0 or any AI coding tool. Prefer the
finished markup? .
Build a two-factor enrolment card with Tailwind CSS, max-w-xl on a soft neutral background. Above the fold: a small uppercase "Security" eyebrow, a headline, and a line naming a few authenticator apps. The pairing panel puts a square QR placeholder (a 5-column grid of small rounded squares inside a bordered tile) beside a column holding a "Can't scan it?" hint, the setup key in a monospaced pill with a Copy button, and a six-digit confirmation input with wide letter-spacing. Footer row: a muted Cancel link and a dark "Turn on" button. Below it, a hidden success panel with a green confirmation strip, a grid of one-time backup codes, and a "Copy all codes" button. Then add JavaScript that copies the setup key to the clipboard and flips the button label to "Copied" for a moment, strips non-digits from the confirmation field and keeps the "Turn on" button disabled until six digits are present, and on confirm swaps the pairing panel for the backup-code panel and wires its copy button to put every code on the clipboard as separate lines.
var secret = document.getElementById("secret");
var copySecret = document.getElementById("copySecret");
var code = document.getElementById("code");
var verify = document.getElementById("verify");
var pairing = document.getElementById("pairing");
var done = document.getElementById("done");
var copyCodes = document.getElementById("copyCodes");
// One helper so both copy buttons behave the same way.
function copy(text, button, label) {
navigator.clipboard.writeText(text).then(function () {
var original = button.textContent;
button.textContent = label;
setTimeout(function () { button.textContent = original; }, 1600);
});
}
copySecret.addEventListener("click", function () {
copy(secret.textContent.replace(/\s/g, ""), copySecret, "Copied");
});
code.addEventListener("input", function () {
code.value = code.value.replace(/\D/g, "").slice(0, 6);
var ready = code.value.length === 6;
verify.disabled = !ready;
verify.classList.toggle("opacity-40", !ready);
verify.classList.toggle("cursor-not-allowed", !ready);
});
verify.addEventListener("click", function () {
// Enrolment succeeded — the recovery codes are the last thing left to do.
pairing.classList.add("hidden");
done.classList.remove("hidden");
});
copyCodes.addEventListener("click", function () {
var all = [].slice.call(document.querySelectorAll("#codes span")).map(function (s) {
return s.textContent;
});
copy(all.join("\n"), copyCodes, "Copied all codes");
});