styles.css
* {
margin: 0;
box-sizing: border-box;
}
body {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: radial-gradient(ellipse at 60% 20%, #1a1025, #09090b 70%);
font-family: system-ui, sans-serif;
}
.card {
width: 380px;
padding: 36px;
border-radius: 24px;
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(40px);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
gap: 14px;
}
.preview {
height: 190px;
padding: 24px;
border-radius: 16px;
background: linear-gradient(135deg, #1c1c2e, #2d1f3d, #3b2050);
display: flex;
flex-direction: column;
justify-content: space-between;
}
.chip {
width: 40px;
height: 30px;
border-radius: 6px;
background: linear-gradient(135deg, #e2b865, #c9a24d);
}
.number {
font-size: 20px;
font-weight: 600;
color: rgba(255, 255, 255, 0.85);
letter-spacing: 3px;
}
.details {
display: flex;
justify-content: space-between;
font-size: 14px;
color: rgba(255, 255, 255, 0.5);
}
.details small {
display: block;
font-size: 9px;
color: rgba(255, 255, 255, 0.25);
letter-spacing: 1px;
margin-bottom: 2px;
}
label {
display: block;
font-size: 12px;
font-weight: 500;
color: rgba(255, 255, 255, 0.4);
margin-bottom: 6px;
}
input {
width: 100%;
padding: 12px 14px;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.07);
background: rgba(255, 255, 255, 0.03);
color: #e0e7ff;
font-size: 14px;
outline: none;
}
input:focus {
border-color: rgba(99, 102, 241, 0.4);
box-shadow: 0 0 20px rgba(99, 102, 241, 0.1);
}
.row {
display: flex;
gap: 12px;
}
.row > * {
flex: 1;
}
.submit {
padding: 14px;
border: none;
border-radius: 12px;
font-size: 15px;
font-weight: 600;
color: #fff;
background: linear-gradient(135deg, #312e81, #4338ca, #6366f1);
box-shadow: 0 4px 20px rgba(67, 56, 202, 0.3);
}
script.js
const formatters = {
card(value) {
const digits = value.replace(/\D/g, "").slice(0, 16);
return digits.replace(/(.{4})/g, "$1 ").trim();
},
expiry(value) {
const digits = value.replace(/\D/g, "").slice(0, 4);
if (digits.length < 2) return digits;
return digits.slice(0, 2) + " / " + digits.slice(2);
},
digits(value) {
return value.replace(/\D/g, "");
},
};
const displayFormatters = {
card(value) {
const digits = value.replace(/\D/g, "");
const padded = (digits + "•".repeat(16)).slice(0, 16);
return padded.replace(/(.{4})/g, "$1 ").trim();
},
expiry(value) {
const digits = value.replace(/\D/g, "");
const padded = (digits + "•".repeat(4)).slice(0, 4);
return padded.slice(0, 2) + " / " + padded.slice(2);
},
digits(value) {
const digits = value.replace(/\D/g, "");
return (digits + "•".repeat(3)).slice(0, 3);
},
};
const cardDisplay = {
card: document.querySelector(".number"),
expiry: document.querySelector(".expiry"),
digits: document.querySelector(".cvc"),
update(field, value) {
const formatter = displayFormatters[field];
const element = this[field];
if (formatter && element) {
element.textContent = formatter(value);
}
},
};
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("[data-format]").forEach((input) => {
const field = input.dataset.format;
const format = formatters[field];
if (!format) return;
input.addEventListener("input", () => {
const cursor = input.selectionStart;
const before = input.value.length;
input.value = format(input.value);
const after = input.value.length;
input.setSelectionRange(cursor + after - before, cursor + after - before);
cardDisplay.update(field, input.value);
});
});
});