Light JSMIT licensed

Payment Method Settings Card

A premium payment-methods panel: a glossy 3D card preview that mirrors the add-card form as you type and flips to show the CVC when that field is focused, auto-formatted card number and expiry with brand detection (Visa, Mastercard, Amex), saved methods as radio rows where :has() highlights the default, and an 'expires soon' warning. Uses the right autocomplete tokens so browsers and password managers can autofill.

Live Demo
Plain CSSTailwind v4
Try it

Mosaic by Cruip: an admin template with account, billing and app settings pages · $69

Cruip · Mosaic by Cruip · Affiliate link

View Mosaic dashboard

The code

<section class="csp-05">
  <div class="csp-05__panel">
    <header class="csp-05__head">
      <h1>Payment methods</h1>
      <p>Your default method is charged on Nov 14 for the Pro plan.</p>
    </header>

    <div class="csp-05__stage" aria-hidden="true">
      <div class="csp-05__cc">
        <div class="csp-05__face csp-05__face--front">
          <div class="csp-05__ccrow"><span class="csp-05__chip"></span><span class="csp-05__brand" data-brand>VISA</span></div>
          <span class="csp-05__num" data-pnum>•••• •••• •••• 4242</span>
          <div class="csp-05__ccrow csp-05__ccrow--meta"><span><small>Card holder</small><b data-pname>AVA MITCHELL</b></span><span><small>Expires</small><b data-pexp>08/28</b></span></div>
        </div>
        <div class="csp-05__face csp-05__face--back">
          <span class="csp-05__stripe"></span>
          <span class="csp-05__cvcbox"><small>CVC</small><b data-pcvc>•••</b></span>
        </div>
      </div>
    </div>

    <fieldset class="csp-05__methods">
      <legend class="csp-05__sr">Saved payment methods</legend>
      <label class="csp-05__method">
        <input type="radio" name="csp05-m" checked>
        <span class="csp-05__logo csp-05__logo--visa">VISA</span>
        <span class="csp-05__mtext"><b>Visa ending 4242</b><small>Expires 08/2028</small></span>
        <span class="csp-05__tag">Default</span>
      </label>
      <label class="csp-05__method">
        <input type="radio" name="csp05-m">
        <span class="csp-05__logo csp-05__logo--mc"><i></i><i></i></span>
        <span class="csp-05__mtext"><b>Mastercard ending 8210</b><small class="csp-05__warn">Expires 11/2026 · update soon</small></span>
        <span class="csp-05__tag">Default</span>
      </label>
      <label class="csp-05__method">
        <input type="radio" name="csp05-m">
        <span class="csp-05__logo csp-05__logo--bank">BANK</span>
        <span class="csp-05__mtext"><b>US bank account ••6789</b><small>ACH · verified</small></span>
        <span class="csp-05__tag">Default</span>
      </label>
    </fieldset>

    <details class="csp-05__add">
      <summary>Add a new card</summary>
      <form class="csp-05__form" data-form>
        <label class="csp-05__field csp-05__field--full"><span>Card number</span><input data-num inputmode="numeric" autocomplete="cc-number" placeholder="1234 1234 1234 1234" maxlength="19" required></label>
        <label class="csp-05__field"><span>Expiry</span><input data-exp inputmode="numeric" autocomplete="cc-exp" placeholder="MM / YY" maxlength="7" required></label>
        <label class="csp-05__field"><span>CVC</span><input data-cvc inputmode="numeric" autocomplete="cc-csc" placeholder="123" maxlength="4" required></label>
        <label class="csp-05__field csp-05__field--full"><span>Name on card</span><input data-name autocomplete="cc-name" placeholder="Ava Mitchell" required></label>
        <label class="csp-05__check csp-05__field--full"><input type="checkbox" checked> Set as default payment method</label>
        <button class="csp-05__btn csp-05__field--full" type="submit">Save card</button>
        <p class="csp-05__secure csp-05__field--full">Encrypted and processed by our PCI DSS Level 1 payment partner.</p>
      </form>
    </details>
  </div>
</section>
.csp-05,
.csp-05 *,
.csp-05 *::before,
.csp-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.csp-05 {
  --bg: oklch(0.14 0.012 285);
  --surface: oklch(0.19 0.015 285);
  --raised: oklch(0.23 0.018 285);
  --text: oklch(0.96 0.005 285);
  --muted: oklch(0.7 0.015 285);
  --line: oklch(0.3 0.02 285);
  --accent: oklch(0.74 0.15 295);
  --warn: oklch(0.8 0.14 75);
  color-scheme: dark;
  display: grid;
  align-items: center;
  justify-items: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: 40px 20px;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: radial-gradient(70% 50% at 50% 0%,oklch(0.28 0.08 295/.6),transparent 70%),var(--bg);
  color: var(--text);
  accent-color: var(--accent);
  width: 100%;
}

.csp-05__sr {
  position: absolute!important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.csp-05__panel {
  width: min(480px,100%);
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: 24px;
  background: var(--surface);
  box-shadow: 0 40px 80px -40px oklch(0 0 0/.8);
}

.csp-05__head h1 {
  font: 700 22px/1.15 system-ui,sans-serif;
  letter-spacing: -.02em;
}

.csp-05__head p {
  font: 400 14px/1.5 system-ui,sans-serif;
  color: var(--muted);
  margin-top: 6px;
}

.csp-05__stage {
  perspective: 1200px;
}

.csp-05__cc {
  position: relative;
  aspect-ratio: 1.586;
  border-radius: 18px;
  transform-style: preserve-3d;
  transition: rotate .7s cubic-bezier(.3,.7,.2,1);
}

.csp-05:has([data-cvc]:focus) .csp-05__cc {
  rotate: y 180deg;
}

.csp-05__face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 22px;
  border-radius: inherit;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
  color: #fff;
  background: radial-gradient(120% 90% at 0% 0%,oklch(0.6 0.2 300),transparent 55%),radial-gradient(90% 90% at 100% 100%,oklch(0.55 0.17 250),transparent 60%),oklch(0.28 0.08 280);
  box-shadow: inset 0 1px 0 oklch(1 0 0/.25),0 24px 48px -20px oklch(0.4 0.2 290/.7);
}

.csp-05__face::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg,transparent 30%,oklch(1 0 0/.18) 45%,transparent 60%);
  pointer-events: none;
}

.csp-05__face--back {
  rotate: y 180deg;
  justify-content: flex-start;
  gap: 22px;
  padding: 22px 0;
}

.csp-05__ccrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.csp-05__ccrow--meta {
  align-items: flex-end;
}

.csp-05__ccrow--meta span {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.csp-05__ccrow small {
  font: 600 9.5px/1 system-ui,sans-serif;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .7;
}

.csp-05__ccrow b {
  font: 600 14px/1 ui-monospace,Menlo,monospace;
  letter-spacing: .06em;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.csp-05__chip {
  width: 42px;
  height: 32px;
  border-radius: 7px;
  background: linear-gradient(135deg,oklch(0.88 0.08 85),oklch(0.72 0.1 75));
  box-shadow: inset 0 0 0 1px oklch(0.6 0.08 75/.6);
}

.csp-05__brand {
  font: italic 800 20px/1 system-ui,sans-serif;
  letter-spacing: .02em;
}

.csp-05__num {
  font: 500 clamp(16px,5vw,21px)/1 ui-monospace,Menlo,monospace;
  letter-spacing: .12em;
  text-shadow: 0 1px 1px oklch(0 0 0/.3);
}

.csp-05__stripe {
  height: 42px;
  background: oklch(0.12 0.01 285);
}

.csp-05__cvcbox {
  align-self: flex-end;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-right: 22px;
  padding: 8px 12px;
  border-radius: 6px;
  background: #fff;
  color: oklch(0.2 0.02 285);
}

.csp-05__cvcbox b {
  font: 700 15px/1 ui-monospace,Menlo,monospace;
  letter-spacing: .1em;
}

.csp-05__methods {
  border: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.csp-05__method {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 64px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--raised);
  cursor: pointer;
  transition: border-color .2s,background .2s;
}

.csp-05__method:hover {
  border-color: oklch(0.4 0.03 285);
}

.csp-05__method:has(input:checked) {
  border-color: var(--accent);
  background: oklch(0.74 0.15 295/.08);
}

.csp-05__method:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.csp-05__method input {
  width: 18px;
  height: 18px;
  flex: none;
}

.csp-05__logo {
  flex: none;
  display: grid;
  place-items: center;
  width: 46px;
  height: 32px;
  border-radius: 6px;
  font: italic 800 11px/1 system-ui,sans-serif;
  background: #fff;
  color: oklch(0.35 0.15 265);
}

.csp-05__logo--mc {
  position: relative;
  background: oklch(0.22 0.01 285);
}

.csp-05__logo--mc i {
  position: absolute;
  top: 8px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: oklch(0.63 0.22 27);
}

.csp-05__logo--mc i:first-child {
  left: 10px;
}

.csp-05__logo--mc i:last-child {
  left: 20px;
  background: oklch(0.8 0.16 75);
  mix-blend-mode: screen;
}

.csp-05__logo--bank {
  font-style: normal;
  font-size: 9.5px;
  letter-spacing: .08em;
  color: var(--text);
  background: oklch(0.3 0.02 285);
}

.csp-05__mtext {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.csp-05__mtext b {
  font: 600 14px/1.2 system-ui,sans-serif;
}

.csp-05__mtext small {
  font: 400 12.5px/1.2 system-ui,sans-serif;
  color: var(--muted);
}

.csp-05__mtext .csp-05__warn {
  color: var(--warn);
}

.csp-05__tag {
  display: none;
  padding: 4px 9px;
  border-radius: 999px;
  font: 650 11px/1 system-ui,sans-serif;
  color: oklch(0.2 0.05 295);
  background: var(--accent);
}

.csp-05__method:has(input:checked) .csp-05__tag {
  display: inline-block;
}

.csp-05__add {
  border: 1px dashed var(--line);
  border-radius: 14px;
  transition: border-color .2s;
}

.csp-05__add[open] {
  border-style: solid;
}

.csp-05__add summary {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  padding: 0 16px;
  font: 600 14px/1 system-ui,sans-serif;
  cursor: pointer;
  list-style: none;
  border-radius: 14px;
}

.csp-05__add summary::-webkit-details-marker {
  display: none;
}

.csp-05__add summary::before {
  content: "+";
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: oklch(1 0 0/.08);
  font: 500 16px/1 system-ui,sans-serif;
  transition: rotate .25s;
}

.csp-05__add[open] summary::before {
  rotate: 45deg;
}

.csp-05__add summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.csp-05__form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  padding: 4px 16px 18px;
}

.csp-05__field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  font: 550 12.5px/1 system-ui,sans-serif;
  color: var(--muted);
}

.csp-05__field--full {
  grid-column: 1/-1;
}

.csp-05__field input {
  min-height: 46px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--bg);
  color: var(--text);
  font: 500 15px/1 ui-monospace,Menlo,monospace;
  letter-spacing: .04em;
  transition: border-color .15s,box-shadow .15s;
}

.csp-05__field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px oklch(0.74 0.15 295/.2);
}

.csp-05__field input:user-invalid {
  border-color: oklch(0.65 0.2 25);
}

.csp-05__check {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 32px;
  font: 500 13.5px/1 system-ui,sans-serif;
  color: var(--text);
}

.csp-05__check input {
  width: 18px;
  height: 18px;
}

.csp-05__btn {
  min-height: 48px;
  border: 0;
  border-radius: 12px;
  font: 650 15px/1 system-ui,sans-serif;
  color: oklch(0.18 0.05 295);
  background: var(--accent);
  cursor: pointer;
  transition: filter .15s,transform .1s;
}

.csp-05__btn:hover {
  filter: brightness(1.08);
}

.csp-05__btn:active {
  transform: scale(.99);
}

.csp-05__btn:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 3px;
}

.csp-05__secure {
  font: 400 12px/1.4 system-ui,sans-serif;
  color: var(--muted);
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .csp-05__cc {
    transition: none;
  }
}
(function () {
  var root = document.querySelector('.csp-05');
  var num = root.querySelector('[data-num]'), exp = root.querySelector('[data-exp]');
  var cvc = root.querySelector('[data-cvc]'), name = root.querySelector('[data-name]');
  var pnum = root.querySelector('[data-pnum]'), pexp = root.querySelector('[data-pexp]');
  var pcvc = root.querySelector('[data-pcvc]'), pname = root.querySelector('[data-pname]');
  var brand = root.querySelector('[data-brand]');

  function detect(d) {
    if (/^3[47]/.test(d)) return 'AMEX';
    if (/^(5[1-5]|2[2-7])/.test(d)) return 'MASTERCARD';
    if (/^4/.test(d)) return 'VISA';
    if (/^6(011|5)/.test(d)) return 'DISCOVER';
    return 'CARD';
  }
  num.addEventListener('input', function () {
    var d = num.value.replace(/\D/g, '').slice(0, 16);
    var b = detect(d), groups = b === 'AMEX' ? [4, 6, 5] : [4, 4, 4, 4], out = [], i = 0;
    groups.forEach(function (g) { if (d.slice(i, i + g)) out.push(d.slice(i, i + g)); i += g; });
    num.value = out.join(' ');
    brand.textContent = d ? b : 'VISA';
    var mask = (b === 'AMEX' ? '•••• •••••• •••••' : '•••• •••• •••• ••••');
    pnum.textContent = d ? num.value + mask.slice(num.value.length) : '•••• •••• •••• 4242';
  });
  exp.addEventListener('input', function (e) {
    var d = exp.value.replace(/\D/g, '').slice(0, 4);
    if (d.length === 1 && d > 1) d = '0' + d;
    exp.value = d.length > 2 ? d.slice(0, 2) + ' / ' + d.slice(2) : (d.length === 2 && e.inputType !== 'deleteContentBackward' ? d + ' / ' : d);
    pexp.textContent = d ? (d.slice(0, 2) + '/' + (d.slice(2) || 'YY')) : '08/28';
  });
  cvc.addEventListener('input', function () {
    cvc.value = cvc.value.replace(/\D/g, '').slice(0, 4);
    pcvc.textContent = cvc.value || '•••';
  });
  name.addEventListener('input', function () { pname.textContent = name.value.toUpperCase() || 'AVA MITCHELL'; });

  root.querySelector('[data-form]').addEventListener('submit', function (e) {
    e.preventDefault();
    var btn = e.target.querySelector('button');
    btn.textContent = 'Card saved';
    setTimeout(function () { btn.textContent = 'Save card'; }, 1800);
  });
})();
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Settings Page from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Payment Method Settings Card
Source: https://codefronts.com/layouts/css-settings-pages/payment-method-settings-card/

A premium payment-methods panel: a glossy 3D card preview that mirrors the add-card form as you type and flips to show the CVC when that field is focused, auto-formatted card number and expiry with brand detection (Visa, Mastercard, Amex), saved methods as radio rows where :has() highlights the default, and an 'expires soon' warning. Uses the right autocomplete tokens so browsers and password managers can autofill.
## HTML
```html
<section class="csp-05">
  <div class="csp-05__panel">
    <header class="csp-05__head">
      <h1>Payment methods</h1>
      <p>Your default method is charged on Nov 14 for the Pro plan.</p>
    </header>

    <div class="csp-05__stage" aria-hidden="true">
      <div class="csp-05__cc">
        <div class="csp-05__face csp-05__face--front">
          <div class="csp-05__ccrow"><span class="csp-05__chip"></span><span class="csp-05__brand" data-brand>VISA</span></div>
          <span class="csp-05__num" data-pnum>•••• •••• •••• 4242</span>
          <div class="csp-05__ccrow csp-05__ccrow--meta"><span><small>Card holder</small><b data-pname>AVA MITCHELL</b></span><span><small>Expires</small><b data-pexp>08/28</b></span></div>
        </div>
        <div class="csp-05__face csp-05__face--back">
          <span class="csp-05__stripe"></span>
          <span class="csp-05__cvcbox"><small>CVC</small><b data-pcvc>•••</b></span>
        </div>
      </div>
    </div>

    <fieldset class="csp-05__methods">
      <legend class="csp-05__sr">Saved payment methods</legend>
      <label class="csp-05__method">
        <input type="radio" name="csp05-m" checked>
        <span class="csp-05__logo csp-05__logo--visa">VISA</span>
        <span class="csp-05__mtext"><b>Visa ending 4242</b><small>Expires 08/2028</small></span>
        <span class="csp-05__tag">Default</span>
      </label>
      <label class="csp-05__method">
        <input type="radio" name="csp05-m">
        <span class="csp-05__logo csp-05__logo--mc"><i></i><i></i></span>
        <span class="csp-05__mtext"><b>Mastercard ending 8210</b><small class="csp-05__warn">Expires 11/2026 · update soon</small></span>
        <span class="csp-05__tag">Default</span>
      </label>
      <label class="csp-05__method">
        <input type="radio" name="csp05-m">
        <span class="csp-05__logo csp-05__logo--bank">BANK</span>
        <span class="csp-05__mtext"><b>US bank account ••6789</b><small>ACH · verified</small></span>
        <span class="csp-05__tag">Default</span>
      </label>
    </fieldset>

    <details class="csp-05__add">
      <summary>Add a new card</summary>
      <form class="csp-05__form" data-form>
        <label class="csp-05__field csp-05__field--full"><span>Card number</span><input data-num inputmode="numeric" autocomplete="cc-number" placeholder="1234 1234 1234 1234" maxlength="19" required></label>
        <label class="csp-05__field"><span>Expiry</span><input data-exp inputmode="numeric" autocomplete="cc-exp" placeholder="MM / YY" maxlength="7" required></label>
        <label class="csp-05__field"><span>CVC</span><input data-cvc inputmode="numeric" autocomplete="cc-csc" placeholder="123" maxlength="4" required></label>
        <label class="csp-05__field csp-05__field--full"><span>Name on card</span><input data-name autocomplete="cc-name" placeholder="Ava Mitchell" required></label>
        <label class="csp-05__check csp-05__field--full"><input type="checkbox" checked> Set as default payment method</label>
        <button class="csp-05__btn csp-05__field--full" type="submit">Save card</button>
        <p class="csp-05__secure csp-05__field--full">Encrypted and processed by our PCI DSS Level 1 payment partner.</p>
      </form>
    </details>
  </div>
</section>
```
## CSS
```css
.csp-05,
.csp-05 *,
.csp-05 *::before,
.csp-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.csp-05 {
  --bg: oklch(0.14 0.012 285);
  --surface: oklch(0.19 0.015 285);
  --raised: oklch(0.23 0.018 285);
  --text: oklch(0.96 0.005 285);
  --muted: oklch(0.7 0.015 285);
  --line: oklch(0.3 0.02 285);
  --accent: oklch(0.74 0.15 295);
  --warn: oklch(0.8 0.14 75);
  color-scheme: dark;
  display: grid;
  align-items: center;
  justify-items: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: 40px 20px;
  font-family: system-ui,'Segoe UI',sans-serif;
  background: radial-gradient(70% 50% at 50% 0%,oklch(0.28 0.08 295/.6),transparent 70%),var(--bg);
  color: var(--text);
  accent-color: var(--accent);
  width: 100%;
}

.csp-05__sr {
  position: absolute!important;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.csp-05__panel {
  width: min(480px,100%);
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 28px;
  border: 1px solid var(--line);
  border-radius: 24px;
  background: var(--surface);
  box-shadow: 0 40px 80px -40px oklch(0 0 0/.8);
}

.csp-05__head h1 {
  font: 700 22px/1.15 system-ui,sans-serif;
  letter-spacing: -.02em;
}

.csp-05__head p {
  font: 400 14px/1.5 system-ui,sans-serif;
  color: var(--muted);
  margin-top: 6px;
}

.csp-05__stage {
  perspective: 1200px;
}

.csp-05__cc {
  position: relative;
  aspect-ratio: 1.586;
  border-radius: 18px;
  transform-style: preserve-3d;
  transition: rotate .7s cubic-bezier(.3,.7,.2,1);
}

.csp-05:has([data-cvc]:focus) .csp-05__cc {
  rotate: y 180deg;
}

.csp-05__face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 22px;
  border-radius: inherit;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
  color: #fff;
  background: radial-gradient(120% 90% at 0% 0%,oklch(0.6 0.2 300),transparent 55%),radial-gradient(90% 90% at 100% 100%,oklch(0.55 0.17 250),transparent 60%),oklch(0.28 0.08 280);
  box-shadow: inset 0 1px 0 oklch(1 0 0/.25),0 24px 48px -20px oklch(0.4 0.2 290/.7);
}

.csp-05__face::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg,transparent 30%,oklch(1 0 0/.18) 45%,transparent 60%);
  pointer-events: none;
}

.csp-05__face--back {
  rotate: y 180deg;
  justify-content: flex-start;
  gap: 22px;
  padding: 22px 0;
}

.csp-05__ccrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.csp-05__ccrow--meta {
  align-items: flex-end;
}

.csp-05__ccrow--meta span {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.csp-05__ccrow small {
  font: 600 9.5px/1 system-ui,sans-serif;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .7;
}

.csp-05__ccrow b {
  font: 600 14px/1 ui-monospace,Menlo,monospace;
  letter-spacing: .06em;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.csp-05__chip {
  width: 42px;
  height: 32px;
  border-radius: 7px;
  background: linear-gradient(135deg,oklch(0.88 0.08 85),oklch(0.72 0.1 75));
  box-shadow: inset 0 0 0 1px oklch(0.6 0.08 75/.6);
}

.csp-05__brand {
  font: italic 800 20px/1 system-ui,sans-serif;
  letter-spacing: .02em;
}

.csp-05__num {
  font: 500 clamp(16px,5vw,21px)/1 ui-monospace,Menlo,monospace;
  letter-spacing: .12em;
  text-shadow: 0 1px 1px oklch(0 0 0/.3);
}

.csp-05__stripe {
  height: 42px;
  background: oklch(0.12 0.01 285);
}

.csp-05__cvcbox {
  align-self: flex-end;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-right: 22px;
  padding: 8px 12px;
  border-radius: 6px;
  background: #fff;
  color: oklch(0.2 0.02 285);
}

.csp-05__cvcbox b {
  font: 700 15px/1 ui-monospace,Menlo,monospace;
  letter-spacing: .1em;
}

.csp-05__methods {
  border: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.csp-05__method {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 64px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--raised);
  cursor: pointer;
  transition: border-color .2s,background .2s;
}

.csp-05__method:hover {
  border-color: oklch(0.4 0.03 285);
}

.csp-05__method:has(input:checked) {
  border-color: var(--accent);
  background: oklch(0.74 0.15 295/.08);
}

.csp-05__method:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.csp-05__method input {
  width: 18px;
  height: 18px;
  flex: none;
}

.csp-05__logo {
  flex: none;
  display: grid;
  place-items: center;
  width: 46px;
  height: 32px;
  border-radius: 6px;
  font: italic 800 11px/1 system-ui,sans-serif;
  background: #fff;
  color: oklch(0.35 0.15 265);
}

.csp-05__logo--mc {
  position: relative;
  background: oklch(0.22 0.01 285);
}

.csp-05__logo--mc i {
  position: absolute;
  top: 8px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: oklch(0.63 0.22 27);
}

.csp-05__logo--mc i:first-child {
  left: 10px;
}

.csp-05__logo--mc i:last-child {
  left: 20px;
  background: oklch(0.8 0.16 75);
  mix-blend-mode: screen;
}

.csp-05__logo--bank {
  font-style: normal;
  font-size: 9.5px;
  letter-spacing: .08em;
  color: var(--text);
  background: oklch(0.3 0.02 285);
}

.csp-05__mtext {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.csp-05__mtext b {
  font: 600 14px/1.2 system-ui,sans-serif;
}

.csp-05__mtext small {
  font: 400 12.5px/1.2 system-ui,sans-serif;
  color: var(--muted);
}

.csp-05__mtext .csp-05__warn {
  color: var(--warn);
}

.csp-05__tag {
  display: none;
  padding: 4px 9px;
  border-radius: 999px;
  font: 650 11px/1 system-ui,sans-serif;
  color: oklch(0.2 0.05 295);
  background: var(--accent);
}

.csp-05__method:has(input:checked) .csp-05__tag {
  display: inline-block;
}

.csp-05__add {
  border: 1px dashed var(--line);
  border-radius: 14px;
  transition: border-color .2s;
}

.csp-05__add[open] {
  border-style: solid;
}

.csp-05__add summary {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  padding: 0 16px;
  font: 600 14px/1 system-ui,sans-serif;
  cursor: pointer;
  list-style: none;
  border-radius: 14px;
}

.csp-05__add summary::-webkit-details-marker {
  display: none;
}

.csp-05__add summary::before {
  content: "+";
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: oklch(1 0 0/.08);
  font: 500 16px/1 system-ui,sans-serif;
  transition: rotate .25s;
}

.csp-05__add[open] summary::before {
  rotate: 45deg;
}

.csp-05__add summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.csp-05__form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  padding: 4px 16px 18px;
}

.csp-05__field {
  display: flex;
  flex-direction: column;
  gap: 7px;
  font: 550 12.5px/1 system-ui,sans-serif;
  color: var(--muted);
}

.csp-05__field--full {
  grid-column: 1/-1;
}

.csp-05__field input {
  min-height: 46px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: var(--bg);
  color: var(--text);
  font: 500 15px/1 ui-monospace,Menlo,monospace;
  letter-spacing: .04em;
  transition: border-color .15s,box-shadow .15s;
}

.csp-05__field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 4px oklch(0.74 0.15 295/.2);
}

.csp-05__field input:user-invalid {
  border-color: oklch(0.65 0.2 25);
}

.csp-05__check {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 32px;
  font: 500 13.5px/1 system-ui,sans-serif;
  color: var(--text);
}

.csp-05__check input {
  width: 18px;
  height: 18px;
}

.csp-05__btn {
  min-height: 48px;
  border: 0;
  border-radius: 12px;
  font: 650 15px/1 system-ui,sans-serif;
  color: oklch(0.18 0.05 295);
  background: var(--accent);
  cursor: pointer;
  transition: filter .15s,transform .1s;
}

.csp-05__btn:hover {
  filter: brightness(1.08);
}

.csp-05__btn:active {
  transform: scale(.99);
}

.csp-05__btn:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 3px;
}

.csp-05__secure {
  font: 400 12px/1.4 system-ui,sans-serif;
  color: var(--muted);
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .csp-05__cc {
    transition: none;
  }
}
```

## JavaScript
```js
(function () {
  var root = document.querySelector('.csp-05');
  var num = root.querySelector('[data-num]'), exp = root.querySelector('[data-exp]');
  var cvc = root.querySelector('[data-cvc]'), name = root.querySelector('[data-name]');
  var pnum = root.querySelector('[data-pnum]'), pexp = root.querySelector('[data-pexp]');
  var pcvc = root.querySelector('[data-pcvc]'), pname = root.querySelector('[data-pname]');
  var brand = root.querySelector('[data-brand]');

  function detect(d) {
    if (/^3[47]/.test(d)) return 'AMEX';
    if (/^(5[1-5]|2[2-7])/.test(d)) return 'MASTERCARD';
    if (/^4/.test(d)) return 'VISA';
    if (/^6(011|5)/.test(d)) return 'DISCOVER';
    return 'CARD';
  }
  num.addEventListener('input', function () {
    var d = num.value.replace(/\D/g, '').slice(0, 16);
    var b = detect(d), groups = b === 'AMEX' ? [4, 6, 5] : [4, 4, 4, 4], out = [], i = 0;
    groups.forEach(function (g) { if (d.slice(i, i + g)) out.push(d.slice(i, i + g)); i += g; });
    num.value = out.join(' ');
    brand.textContent = d ? b : 'VISA';
    var mask = (b === 'AMEX' ? '•••• •••••• •••••' : '•••• •••• •••• ••••');
    pnum.textContent = d ? num.value + mask.slice(num.value.length) : '•••• •••• •••• 4242';
  });
  exp.addEventListener('input', function (e) {
    var d = exp.value.replace(/\D/g, '').slice(0, 4);
    if (d.length === 1 && d > 1) d = '0' + d;
    exp.value = d.length > 2 ? d.slice(0, 2) + ' / ' + d.slice(2) : (d.length === 2 && e.inputType !== 'deleteContentBackward' ? d + ' / ' : d);
    pexp.textContent = d ? (d.slice(0, 2) + '/' + (d.slice(2) || 'YY')) : '08/28';
  });
  cvc.addEventListener('input', function () {
    cvc.value = cvc.value.replace(/\D/g, '').slice(0, 4);
    pcvc.textContent = cvc.value || '•••';
  });
  name.addEventListener('input', function () { pname.textContent = name.value.toUpperCase() || 'AVA MITCHELL'; });

  root.querySelector('[data-form]').addEventListener('submit', function (e) {
    e.preventDefault();
    var btn = e.target.querySelector('button');
    btn.textContent = 'Card saved';
    setTimeout(function () { btn.textContent = 'Save card'; }, 1800);
  });
})();
```

How this works

Saved methods are a radio group; each row is a <label> so the whole row is clickable, and .csp-05__method:has(input:checked) paints the selected ring and reveals the 'Default' tag — no JS for selection. The add form lives in a native <details>, giving an accessible disclosure for free.

The card visual is a two-faced element with transform-style:preserve-3d and backface-visibility:hidden. A :has() rule — .csp-05:has([data-cvc]:focus) .csp-05__cc — rotates it 180° so the back with the CVC strip faces the user while they type it. A small script formats the number into groups (4-6-5 for Amex), inserts the expiry slash, detects the brand from the IIN prefix, and mirrors values into the preview. Inputs use inputmode=numeric and autocomplete=cc-number / cc-exp / cc-csc / cc-name.

Techniques used: 3D card flip with preserve-3d + backface-visibility · :has(:focus) driven flip · IIN brand detection · input masking for card number + expiry · autocomplete cc-* tokens + inputmode=numeric · label radio rows with :has(:checked) · native <details> disclosure

.csp-05__cc { transform-style: preserve-3d; transition: rotate .6s; }
.csp-05:has([data-cvc]:focus) .csp-05__cc { rotate: y 180deg; }
.csp-05__face--back { rotate: y 180deg; backface-visibility: hidden; }

Make it yours

  • Replace the demo form with your processor's hosted fields (Stripe Elements, Adyen, Braintree) — keep the preview card and mirror values via their change events.
  • Tint the card by brand: set data-brand on the card and swap the gradient per brand.
  • Add a 'Remove' action per row behind a confirm, and block removing the last default method.
  • Add Apple Pay / Google Pay rows above the cards with the same radio-row pattern.

Gotchas — read before shipping

  • Never send raw card numbers to your own server — use your payment provider's tokenised fields in production for PCI compliance.
  • Use inputmode=numeric, not type=number: number inputs drop leading zeros and show spinners.
  • Keep the caret stable when reformatting; this demo reformats on input and moves the caret to the end, which is fine for typing but consider a caret-preserving mask for editing mid-string.
  • Respect reduced motion — the flip becomes an instant swap.

Browser support

ChromeSafariFirefoxEdge
111+15.4+121+111+

Floor set by :has(), oklch() as this demo is written. The core technique itself goes back further — Chrome 105+.

The individual rotate property is Chrome 104+/Safari 14.1+/Firefox 72+. :has() gates the auto-flip and selection styling.

Search CodeFronts

Loading…