Open demo

CSS code

HTML
<div><span class="lbl">Portrait cards that turn on the Y axis</span>
<ul class="trio">
  <li><article class="fp t-rose" aria-labelledby="fp-ines">
    <div class="fp-inner">
      <div class="fp-face fp-front">
        <div class="fp-cover" aria-hidden="true"></div>
        <span class="fp-av"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false"><rect width="80" height="80" fill="#ffe4e6"/><path d="M22.5 39c0-14 7.6-22 17.5-22s17.5 8 17.5 22v23h-35z" fill="#7a3b1d"/><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#be123c"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#e9b48f"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#e9b48f"/><path d="M25 35.5c1-10.2 7-16.5 15-16.5s14.4 6.3 15 16.5c-6.4-1-12-5-15-10.4-3 5.4-8.6 9.4-15 10.4z" fill="#7a3b1d"/><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/></svg></span>
        <div class="fp-body">
          <h3 class="fp-name" id="fp-ines">Inés Duarte</h3>
          <p class="fp-role">Picture book illustrator</p>
          <ul class="fp-tags" aria-label="Specialities"><li>Gouache</li><li>Editorial</li><li>Lettering</li></ul>
          <button class="fp-turn" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Read bio<span class="sr-only"> of Inés Duarte</span></button>
        </div>
      </div>
      <div class="fp-face fp-back" tabindex="-1">
        <p class="fp-k">About</p>
        <p class="fp-name">Inés Duarte</p>
        <p class="fp-bio">Paints covers for picture books and the odd newspaper op-ed. Sketches on paper, finishes in gouache, scans at 1200 dpi.</p>
        <dl class="fp-facts"><dt>Based in</dt><dd>Porto, Portugal</dd><dt>Books</dt><dd>23 published</dd><dt>Replies</dt><dd>Within two days</dd></dl>
        <ul class="fp-links"><li><a href="mailto:[email protected]"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="M3.5 6.5l8.5 6.5 8.5-6.5"/></svg>Email</a></li><li><a href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.6 3.8 5.6 3.8 9s-1.3 6.4-3.8 9c-2.5-2.6-3.8-5.6-3.8-9S9.5 5.6 12 3z"/></svg>Portfolio</a></li></ul>
        <button class="fp-turn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to front<span class="sr-only"> of Inés Duarte's card</span></button>
      </div>
    </div>
  </article></li>
  <li><article class="fp" aria-labelledby="fp-ravi">
    <div class="fp-inner">
      <div class="fp-face fp-front">
        <div class="fp-cover" aria-hidden="true"></div>
        <span class="fp-av"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false"><rect width="80" height="80" fill="#e0f2fe"/><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#0369a1"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#a86b48"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#a86b48"/><path d="M24.7 35.5c-.8-10.6 6-17.3 15.3-17.3 9.6 0 16 7 15.3 17.3-9.4 0-15-3.4-17.4-8.4-2.4 4.6-6.6 7.6-13.2 8.4z" fill="#1c1310"/><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><g fill="none" stroke="#1f1a17" stroke-width="1.5"><circle cx="34.5" cy="38.5" r="4.4"/><circle cx="45.5" cy="38.5" r="4.4"/><path d="M38.9 38.2h2.2"/></g></svg></span>
        <div class="fp-body">
          <h3 class="fp-name" id="fp-ravi">Ravi Menon</h3>
          <p class="fp-role">Backend engineer, payments</p>
          <ul class="fp-tags" aria-label="Specialities"><li>Go</li><li>Postgres</li><li>Kafka</li></ul>
          <button class="fp-turn" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Read bio<span class="sr-only"> of Ravi Menon</span></button>
        </div>
      </div>
      <div class="fp-face fp-back" tabindex="-1">
        <p class="fp-k">About</p>
        <p class="fp-name">Ravi Menon</p>
        <p class="fp-bio">Moves money between 31 currencies without losing a cent. Writes the incident reviews people actually read.</p>
        <dl class="fp-facts"><dt>Based in</dt><dd>Kochi, India</dd><dt>On call</dt><dd>Week 2 of 6</dd><dt>Talks</dt><dd>4 this year</dd></dl>
        <ul class="fp-links"><li><a href="mailto:[email protected]"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="M3.5 6.5l8.5 6.5 8.5-6.5"/></svg>Email</a></li><li><a href="#"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>GitHub</a></li></ul>
        <button class="fp-turn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to front<span class="sr-only"> of Ravi Menon's card</span></button>
      </div>
    </div>
  </article></li>
  <li><article class="fp t-teal" aria-labelledby="fp-freya">
    <div class="fp-inner">
      <div class="fp-face fp-front">
        <div class="fp-cover" aria-hidden="true"></div>
        <span class="fp-av"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false"><rect width="80" height="80" fill="#ccfbf1"/><circle cx="40" cy="17.5" r="7.5" fill="#c9a063"/><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#0f766e"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#f3d2b3"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#f3d2b3"/><path d="M25 36.5c0-10.4 6.6-17 15-17s15 6.6 15 17c-4.4-3.4-9.6-8-15-8s-10.6 4.6-15 8z" fill="#c9a063"/><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/></svg></span>
        <div class="fp-body">
          <h3 class="fp-name" id="fp-freya">Freya Lindahl</h3>
          <p class="fp-role">User researcher</p>
          <ul class="fp-tags" aria-label="Specialities"><li>Interviews</li><li>Diary studies</li><li>Surveys</li></ul>
          <button class="fp-turn" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Read bio<span class="sr-only"> of Freya Lindahl</span></button>
        </div>
      </div>
      <div class="fp-face fp-back" tabindex="-1">
        <p class="fp-k">About</p>
        <p class="fp-name">Freya Lindahl</p>
        <p class="fp-bio">Talks to six customers every week and turns the notes into decisions. Keeps a public log of everything she got wrong.</p>
        <dl class="fp-facts"><dt>Based in</dt><dd>Malmö, Sweden</dd><dt>Interviews</dt><dd>412 so far</dd><dt>Languages</dt><dd>Swedish, English</dd></dl>
        <ul class="fp-links"><li><a href="mailto:[email protected]"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="M3.5 6.5l8.5 6.5 8.5-6.5"/></svg>Email</a></li><li><a href="#"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false"><path d="M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5zM3 9.75h4V21H3zM9.5 9.75h3.8v1.6h.06c.53-1 1.83-2.05 3.77-2.05 4.03 0 4.77 2.6 4.77 6V21h-4v-5c0-1.2-.02-2.75-1.68-2.75-1.68 0-1.94 1.3-1.94 2.66V21h-4z"/></svg>LinkedIn</a></li></ul>
        <button class="fp-turn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to front<span class="sr-only"> of Freya Lindahl's card</span></button>
      </div>
    </div>
  </article></li>
</ul></div>

<div class="solo"><span class="lbl">Landscape speaker card that turns on the X axis</span>
<article class="fp fp-x" aria-labelledby="fp-selin">
  <div class="fp-inner">
    <div class="fp-face fp-front">
      <span class="fp-av"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false"><rect width="80" height="80" fill="#ede9fe"/><g fill="#2a1a12"><circle cx="26" cy="38" r="6"/><circle cx="54" cy="38" r="6"/></g><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#7c3aed"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#c98b62"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#c98b62"/><g fill="#2a1a12"><circle cx="27.5" cy="30" r="6.5"/><circle cx="33.5" cy="23.5" r="7"/><circle cx="41" cy="21.5" r="7.5"/><circle cx="48.5" cy="24" r="7"/><circle cx="53" cy="31" r="6"/></g><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/></svg></span>
      <div class="fp-body">
        <h3 class="fp-name" id="fp-selin">Selin Aydın</h3>
        <p class="fp-role">Staff designer, incident tooling</p>
        <p class="fp-talk">Designing for 3 a.m.: interfaces for people who just got paged</p>
        <button class="fp-turn" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Session details<span class="sr-only"> for Selin Aydın</span></button>
      </div>
    </div>
    <div class="fp-face fp-back" tabindex="-1">
      <div>
        <p class="fp-k">Session</p>
        <p class="fp-name">Designing for 3 a.m.</p>
        <dl class="fp-facts"><dt>When</dt><dd>Day 2, 14:30 to 15:10</dd><dt>Where</dt><dd>Room B, the Atrium</dd><dt>Track</dt><dd>Reliability</dd></dl>
      </div>
      <ul class="fp-links"><li><a href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="3.5" y="5" width="17" height="15.5" rx="2.5"/><path d="M3.5 10h17M8 3v4M16 3v4"/></svg>Add to calendar</a></li><li><a href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M10 14a4.5 4.5 0 0 0 6.4 0l3-3a4.5 4.5 0 0 0-6.4-6.4l-1.2 1.2"/><path d="M14 10a4.5 4.5 0 0 0-6.4 0l-3 3a4.5 4.5 0 0 0 6.4 6.4l1.2-1.2"/></svg>Slides</a></li></ul>
      <button class="fp-turn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to speaker<span class="sr-only"> Selin Aydın</span></button>
    </div>
  </div>
</article></div>
CSS
/* Flip profile cards: two faces stacked in one grid cell. A button on each face turns the card (Y axis, or X with .fp-x);
   the hidden face is made inert so it can't be tabbed into, focus moves to the face that comes up, and Escape turns it back.
   Without JavaScript the faces simply stack. Under prefers-reduced-motion the turn becomes a crossfade. */
.fp{--fp-accent:#0369a1;--fp-ink:#0f172a;--fp-muted:#475569;--fp-line:#e2e8f0;--fp-back:#0f172a;--fp-back-ink:#e2e8f0;--fp-back-muted:#a5b4c8;--fp-cover:linear-gradient(120deg,#38bdf8,#6366f1);
  height:100%;perspective:1400px;color:var(--fp-ink)}
.fp :where(h3,p,ul,dl,dd){margin:0;padding:0}
.fp-inner{display:grid;gap:16px;height:100%}
.fp-face{position:relative;display:flex;flex-direction:column;border-radius:22px;overflow:hidden}
.fp-face:focus{outline:none}
.fp-face:focus-visible{outline:3px solid var(--fp-accent);outline-offset:3px}
.fp-front{background:#fff;border:1px solid var(--fp-line);box-shadow:0 24px 40px -30px rgba(15,23,42,.45);text-align:center}
.fp-cover{height:86px;background:var(--fp-cover)}
.fp-av{display:block;width:96px;height:96px;margin:-48px auto 0;border-radius:50%;overflow:hidden;border:4px solid #fff;box-sizing:content-box}
.fp-av svg{display:block;width:100%;height:100%}
.fp-body{display:flex;flex-direction:column;align-items:center;flex:1;padding:12px 22px 22px}
.fp-name{font-size:1.15rem;font-weight:800;letter-spacing:-.01em}
.fp-role{margin-top:4px;font-size:.9rem;color:var(--fp-muted)}
.fp-tags{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;list-style:none;margin:16px 0 20px}
.fp-tags li{padding:4px 10px;border-radius:999px;background:#f1f5f9;color:#334155;font-size:.78rem;font-weight:600}
.fp-turn{display:inline-flex;align-items:center;justify-content:center;gap:8px;margin-top:auto;padding:10px 16px;border:1.5px solid var(--fp-accent);border-radius:12px;background:transparent;color:var(--fp-accent);
  font:inherit;font-size:.88rem;font-weight:700;cursor:pointer;transition:background-color .2s,color .2s}
.fp-turn svg{width:16px;height:16px}
.fp-turn:hover{background:var(--fp-accent);color:#fff}
.fp-turn:focus-visible{outline:3px solid var(--fp-accent);outline-offset:3px}
.fp-back{padding:24px 22px 22px;background:var(--fp-back);color:var(--fp-back-ink);box-shadow:0 24px 40px -26px rgba(15,23,42,.6);text-align:left}
.fp-k{font-size:.72rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--fp-back-muted)}
.fp-back .fp-name{margin-top:6px;color:#fff}
.fp-bio{margin-top:10px;font-size:.9rem;line-height:1.55}
.fp-facts{display:grid;grid-template-columns:auto 1fr;gap:6px 14px;margin-top:16px;font-size:.84rem}
.fp-facts dt{color:var(--fp-back-muted)}
.fp-facts dd{font-weight:600}
.fp-links{display:flex;gap:8px;list-style:none;margin:18px 0 20px}
.fp-links a{display:inline-flex;align-items:center;gap:6px;padding:7px 11px;border-radius:9px;background:rgba(255,255,255,.08);color:#fff;font-size:.82rem;font-weight:600;text-decoration:none}
.fp-links a:hover{background:rgba(255,255,255,.16)}
.fp-links a:focus-visible{outline:3px solid #7dd3fc;outline-offset:2px}
.fp-links svg{width:15px;height:15px}
.fp-back .fp-turn{align-self:flex-start;border-color:#7dd3fc;color:#7dd3fc}
.fp-back .fp-turn:hover{background:#7dd3fc;color:var(--fp-back)}
.fp-back .fp-turn:focus-visible{outline-color:#7dd3fc}
/* the turn, once the script has marked the card ready */
.fp-turn{display:none}
.fp.is-ready .fp-turn{display:inline-flex}
.fp.is-ready .fp-inner{gap:0;transform-style:preserve-3d;transition:transform .7s cubic-bezier(.2,.7,.2,1)}
.fp.is-ready .fp-face{grid-area:1/1;-webkit-backface-visibility:hidden;backface-visibility:hidden}
.fp.is-ready .fp-back{transform:rotateY(180deg)}
.fp.is-flipped .fp-inner{transform:rotateY(180deg)}
.fp-x.is-ready .fp-back{transform:rotateX(180deg)}
.fp-x.is-flipped .fp-inner{transform:rotateX(180deg)}
/* colour themes */
.fp.t-rose{--fp-accent:#be123c;--fp-cover:linear-gradient(120deg,#fb7185,#f59e0b)}
.fp.t-teal{--fp-accent:#0f766e;--fp-cover:linear-gradient(120deg,#2dd4bf,#3b82f6)}
/* landscape speaker card */
.fp-x .fp-front{display:grid;grid-template-columns:auto 1fr;align-items:center;align-content:center;gap:6px 22px;padding:24px;text-align:left}
.fp-x .fp-av{grid-row:span 4;margin:0;border:0;width:104px;height:104px;border-radius:26px}
.fp-x .fp-body{display:contents}
.fp-x .fp-name{font-size:1.25rem}
.fp-talk{grid-column:2;font-size:.95rem;font-weight:600;line-height:1.4}
.fp-x .fp-front .fp-turn{grid-column:2;justify-self:start;margin-top:8px}
.fp-x .fp-back{display:grid;grid-template-columns:1fr auto;align-items:end;gap:16px 24px;padding:24px}
.fp-x .fp-facts{margin-top:12px}
.fp-x .fp-links{margin:0;flex-direction:column}
.fp-x .fp-back .fp-turn{grid-column:1/-1;justify-self:start}
@media (max-width:520px){.fp-x .fp-front,.fp-x .fp-back{grid-template-columns:1fr}.fp-x .fp-av{grid-row:auto}.fp-x .fp-front>*,.fp-talk,.fp-x .fp-front .fp-turn{grid-column:1}}
.fp .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
@media (prefers-reduced-motion:reduce){
  .fp-turn{transition:none}
  .fp.is-ready .fp-inner,.fp.is-flipped .fp-inner{transform:none;transition:none}
  .fp.is-ready .fp-face{-webkit-backface-visibility:visible;backface-visibility:visible;transform:none;transition:opacity .25s,visibility 0s .25s}
  .fp.is-ready:not(.is-flipped) .fp-front,.fp.is-flipped .fp-back{transition:opacity .25s,visibility 0s}
  .fp.is-ready .fp-back,.fp.is-flipped .fp-front{opacity:0;visibility:hidden}
  .fp.is-flipped .fp-back{opacity:1;visibility:visible}
}
JavaScript
/* Flip cards: each .fp-turn turns its card over. The face that is turned away gets the inert attribute (no focus, hidden from
   screen readers). Turning to the back moves focus to the back face so it is read from the top; turning to the front (with its
   button or Escape) returns focus to the button that opened it. */
document.querySelectorAll('.fp').forEach(function (card) {
  var front = card.querySelector('.fp-front'), back = card.querySelector('.fp-back');
  var opener = front.querySelector('.fp-turn');
  function show(flipped, moveFocus) {
    card.classList.toggle('is-flipped', flipped);
    front.inert = flipped; back.inert = !flipped;
    opener.setAttribute('aria-expanded', String(flipped));
    if (moveFocus) (flipped ? back : opener).focus({ preventScroll: true });
  }
  card.querySelectorAll('.fp-turn').forEach(function (btn) {
    btn.addEventListener('click', function () { show(!card.classList.contains('is-flipped'), true); });
  });
  back.addEventListener('keydown', function (e) { if (e.key === 'Escape') show(false, true); });
  card.classList.add('is-ready');
  show(false, false);
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Portrait cards that turn on the Y axis</span>
<ul class="trio row row-cols-1 row-cols-md-3 list-unstyled">
  <li class="col"><article class="fp t-rose" aria-labelledby="fp-ines">
    <div class="fp-inner">
      <div class="card fp-face fp-front">
        <div class="fp-cover" aria-hidden="true"></div>
        <span class="fp-av"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false"><rect width="80" height="80" fill="#ffe4e6"/><path d="M22.5 39c0-14 7.6-22 17.5-22s17.5 8 17.5 22v23h-35z" fill="#7a3b1d"/><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#be123c"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#e9b48f"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#e9b48f"/><path d="M25 35.5c1-10.2 7-16.5 15-16.5s14.4 6.3 15 16.5c-6.4-1-12-5-15-10.4-3 5.4-8.6 9.4-15 10.4z" fill="#7a3b1d"/><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/></svg></span>
        <div class="fp-body">
          <h3 class="fp-name" id="fp-ines">Inés Duarte</h3>
          <p class="fp-role">Picture book illustrator</p>
          <ul class="fp-tags list-unstyled" aria-label="Specialities"><li class="badge rounded-pill">Gouache</li><li class="badge rounded-pill">Editorial</li><li class="badge rounded-pill">Lettering</li></ul>
          <button class="btn fp-turn" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Read bio<span class="visually-hidden"> of Inés Duarte</span></button>
        </div>
      </div>
      <div class="card fp-face fp-back" tabindex="-1">
        <p class="fp-k">About</p>
        <p class="fp-name">Inés Duarte</p>
        <p class="fp-bio">Paints covers for picture books and the odd newspaper op-ed. Sketches on paper, finishes in gouache, scans at 1200 dpi.</p>
        <dl class="fp-facts"><dt>Based in</dt><dd>Porto, Portugal</dd><dt>Books</dt><dd>23 published</dd><dt>Replies</dt><dd>Within two days</dd></dl>
        <ul class="fp-links list-unstyled"><li><a href="mailto:[email protected]"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="M3.5 6.5l8.5 6.5 8.5-6.5"/></svg>Email</a></li><li><a href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.6 3.8 5.6 3.8 9s-1.3 6.4-3.8 9c-2.5-2.6-3.8-5.6-3.8-9S9.5 5.6 12 3z"/></svg>Portfolio</a></li></ul>
        <button class="btn fp-turn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to front<span class="visually-hidden"> of Inés Duarte's card</span></button>
      </div>
    </div>
  </article></li>
  <li class="col"><article class="fp" aria-labelledby="fp-ravi">
    <div class="fp-inner">
      <div class="card fp-face fp-front">
        <div class="fp-cover" aria-hidden="true"></div>
        <span class="fp-av"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false"><rect width="80" height="80" fill="#e0f2fe"/><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#0369a1"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#a86b48"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#a86b48"/><path d="M24.7 35.5c-.8-10.6 6-17.3 15.3-17.3 9.6 0 16 7 15.3 17.3-9.4 0-15-3.4-17.4-8.4-2.4 4.6-6.6 7.6-13.2 8.4z" fill="#1c1310"/><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><g fill="none" stroke="#1f1a17" stroke-width="1.5"><circle cx="34.5" cy="38.5" r="4.4"/><circle cx="45.5" cy="38.5" r="4.4"/><path d="M38.9 38.2h2.2"/></g></svg></span>
        <div class="fp-body">
          <h3 class="fp-name" id="fp-ravi">Ravi Menon</h3>
          <p class="fp-role">Backend engineer, payments</p>
          <ul class="fp-tags list-unstyled" aria-label="Specialities"><li class="badge rounded-pill">Go</li><li class="badge rounded-pill">Postgres</li><li class="badge rounded-pill">Kafka</li></ul>
          <button class="btn fp-turn" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Read bio<span class="visually-hidden"> of Ravi Menon</span></button>
        </div>
      </div>
      <div class="card fp-face fp-back" tabindex="-1">
        <p class="fp-k">About</p>
        <p class="fp-name">Ravi Menon</p>
        <p class="fp-bio">Moves money between 31 currencies without losing a cent. Writes the incident reviews people actually read.</p>
        <dl class="fp-facts"><dt>Based in</dt><dd>Kochi, India</dd><dt>On call</dt><dd>Week 2 of 6</dd><dt>Talks</dt><dd>4 this year</dd></dl>
        <ul class="fp-links list-unstyled"><li><a href="mailto:[email protected]"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="M3.5 6.5l8.5 6.5 8.5-6.5"/></svg>Email</a></li><li><a href="#"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>GitHub</a></li></ul>
        <button class="btn fp-turn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to front<span class="visually-hidden"> of Ravi Menon's card</span></button>
      </div>
    </div>
  </article></li>
  <li class="col"><article class="fp t-teal" aria-labelledby="fp-freya">
    <div class="fp-inner">
      <div class="card fp-face fp-front">
        <div class="fp-cover" aria-hidden="true"></div>
        <span class="fp-av"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false"><rect width="80" height="80" fill="#ccfbf1"/><circle cx="40" cy="17.5" r="7.5" fill="#c9a063"/><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#0f766e"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#f3d2b3"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#f3d2b3"/><path d="M25 36.5c0-10.4 6.6-17 15-17s15 6.6 15 17c-4.4-3.4-9.6-8-15-8s-10.6 4.6-15 8z" fill="#c9a063"/><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/></svg></span>
        <div class="fp-body">
          <h3 class="fp-name" id="fp-freya">Freya Lindahl</h3>
          <p class="fp-role">User researcher</p>
          <ul class="fp-tags list-unstyled" aria-label="Specialities"><li class="badge rounded-pill">Interviews</li><li class="badge rounded-pill">Diary studies</li><li class="badge rounded-pill">Surveys</li></ul>
          <button class="btn fp-turn" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Read bio<span class="visually-hidden"> of Freya Lindahl</span></button>
        </div>
      </div>
      <div class="card fp-face fp-back" tabindex="-1">
        <p class="fp-k">About</p>
        <p class="fp-name">Freya Lindahl</p>
        <p class="fp-bio">Talks to six customers every week and turns the notes into decisions. Keeps a public log of everything she got wrong.</p>
        <dl class="fp-facts"><dt>Based in</dt><dd>Malmö, Sweden</dd><dt>Interviews</dt><dd>412 so far</dd><dt>Languages</dt><dd>Swedish, English</dd></dl>
        <ul class="fp-links list-unstyled"><li><a href="mailto:[email protected]"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="M3.5 6.5l8.5 6.5 8.5-6.5"/></svg>Email</a></li><li><a href="#"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false"><path d="M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5zM3 9.75h4V21H3zM9.5 9.75h3.8v1.6h.06c.53-1 1.83-2.05 3.77-2.05 4.03 0 4.77 2.6 4.77 6V21h-4v-5c0-1.2-.02-2.75-1.68-2.75-1.68 0-1.94 1.3-1.94 2.66V21h-4z"/></svg>LinkedIn</a></li></ul>
        <button class="btn fp-turn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to front<span class="visually-hidden"> of Freya Lindahl's card</span></button>
      </div>
    </div>
  </article></li>
</ul></div>

<div class="solo"><span class="lbl">Landscape speaker card that turns on the X axis</span>
<article class="fp fp-x" aria-labelledby="fp-selin">
  <div class="fp-inner">
    <div class="card fp-face fp-front">
      <span class="fp-av"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false"><rect width="80" height="80" fill="#ede9fe"/><g fill="#2a1a12"><circle cx="26" cy="38" r="6"/><circle cx="54" cy="38" r="6"/></g><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#7c3aed"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#c98b62"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#c98b62"/><g fill="#2a1a12"><circle cx="27.5" cy="30" r="6.5"/><circle cx="33.5" cy="23.5" r="7"/><circle cx="41" cy="21.5" r="7.5"/><circle cx="48.5" cy="24" r="7"/><circle cx="53" cy="31" r="6"/></g><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/></svg></span>
      <div class="fp-body">
        <h3 class="fp-name" id="fp-selin">Selin Aydın</h3>
        <p class="fp-role">Staff designer, incident tooling</p>
        <p class="fp-talk">Designing for 3 a.m.: interfaces for people who just got paged</p>
        <button class="btn fp-turn" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Session details<span class="visually-hidden"> for Selin Aydın</span></button>
      </div>
    </div>
    <div class="card fp-face fp-back" tabindex="-1">
      <div>
        <p class="fp-k">Session</p>
        <p class="fp-name">Designing for 3 a.m.</p>
        <dl class="fp-facts"><dt>When</dt><dd>Day 2, 14:30 to 15:10</dd><dt>Where</dt><dd>Room B, the Atrium</dd><dt>Track</dt><dd>Reliability</dd></dl>
      </div>
      <ul class="fp-links list-unstyled"><li><a href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="3.5" y="5" width="17" height="15.5" rx="2.5"/><path d="M3.5 10h17M8 3v4M16 3v4"/></svg>Add to calendar</a></li><li><a href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M10 14a4.5 4.5 0 0 0 6.4 0l3-3a4.5 4.5 0 0 0-6.4-6.4l-1.2 1.2"/><path d="M14 10a4.5 4.5 0 0 0-6.4 0l-3 3a4.5 4.5 0 0 0 6.4 6.4l1.2-1.2"/></svg>Slides</a></li></ul>
      <button class="btn fp-turn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to speaker<span class="visually-hidden"> Selin Aydın</span></button>
    </div>
  </div>
</article></div>
CSS
/* Flip profile cards on Bootstrap .card, .badge and .btn: two faces stacked in one grid cell. A button on each face turns the card (Y axis, or X with .fp-x);
   the hidden face is made inert so it can't be tabbed into, focus moves to the face that comes up, and Escape turns it back.
   Without JavaScript the faces simply stack. Under prefers-reduced-motion the turn becomes a crossfade. */
.fp{--fp-accent:#0369a1;--fp-ink:#0f172a;--fp-muted:#475569;--fp-line:#e2e8f0;--fp-back:#0f172a;--fp-back-ink:#e2e8f0;--fp-back-muted:#a5b4c8;--fp-cover:linear-gradient(120deg,#38bdf8,#6366f1);
  height:100%;perspective:1400px;color:var(--fp-ink)}
.fp :where(h3,p,ul,dl,dd,dt){margin:0}
.fp .card{--bs-card-border-radius:22px;--bs-card-border-color:var(--fp-line);--bs-card-color:var(--fp-ink)}
.fp-inner{display:grid;gap:16px;height:100%}
.fp-face{position:relative;display:flex;flex-direction:column;border-radius:22px;overflow:hidden}
.fp-face:focus{outline:none}
.fp-face:focus-visible{outline:3px solid var(--fp-accent);outline-offset:3px}
.fp-front{box-shadow:0 24px 40px -30px rgba(15,23,42,.45);text-align:center}
.fp-cover{height:86px;background:var(--fp-cover)}
.fp-av{display:block;width:96px;height:96px;margin:-48px auto 0;border-radius:50%;overflow:hidden;border:4px solid #fff;box-sizing:content-box}
.fp-av svg{display:block;width:100%;height:100%}
.fp-body{display:flex;flex-direction:column;align-items:center;flex:1;padding:12px 22px 22px}
.fp .fp-name{font-size:1.15rem;font-weight:800;letter-spacing:-.01em;line-height:normal}
.fp-role{margin-top:4px;font-size:.9rem;color:var(--fp-muted)}
.fp-tags{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;list-style:none;margin:16px 0 20px}
.fp-tags .badge{--bs-badge-padding-x:10px;--bs-badge-padding-y:4px;--bs-badge-font-size:.78rem;--bs-badge-font-weight:600;--bs-badge-color:#334155;background:#f1f5f9;line-height:normal}
.fp .fp-turn{--bs-btn-padding-x:16px;--bs-btn-padding-y:10px;--bs-btn-font-size:.88rem;--bs-btn-font-weight:700;--bs-btn-line-height:normal;--bs-btn-border-width:1.5px;--bs-btn-border-radius:12px;
  --bs-btn-color:var(--fp-accent);--bs-btn-border-color:var(--fp-accent);--bs-btn-hover-color:#fff;--bs-btn-hover-bg:var(--fp-accent);--bs-btn-hover-border-color:var(--fp-accent);
  --bs-btn-active-color:#fff;--bs-btn-active-bg:var(--fp-accent);--bs-btn-active-border-color:var(--fp-accent);--bs-btn-focus-box-shadow:none;
  align-items:center;justify-content:center;gap:8px;margin-top:auto;transition:background-color .2s,color .2s}
.fp-turn svg{width:16px;height:16px}
.fp-turn:focus-visible{outline:3px solid var(--fp-accent);outline-offset:3px}
.fp .fp-back{--bs-card-bg:var(--fp-back);--bs-card-border-width:0;padding:24px 22px 22px;color:var(--fp-back-ink);box-shadow:0 24px 40px -26px rgba(15,23,42,.6);text-align:left}
.fp-k{font-size:.72rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--fp-back-muted)}
.fp-back .fp-name{margin-top:6px;color:#fff}
.fp-bio{margin-top:10px;font-size:.9rem;line-height:1.55}
.fp-facts{display:grid;grid-template-columns:auto 1fr;gap:6px 14px;margin-top:16px;font-size:.84rem}
.fp-facts dt{color:var(--fp-back-muted)}
.fp-facts dt{font-weight:400}
.fp-facts dd{font-weight:600}
.fp-links{display:flex;gap:8px;list-style:none;margin:18px 0 20px}
.fp-links a{display:inline-flex;align-items:center;gap:6px;padding:7px 11px;border-radius:9px;background:rgba(255,255,255,.08);color:#fff;font-size:.82rem;font-weight:600;text-decoration:none}
.fp-links a:hover{background:rgba(255,255,255,.16);color:#fff}
.fp-links a:focus-visible{outline:3px solid #7dd3fc;outline-offset:2px}
.fp-links svg{width:15px;height:15px}
.fp .fp-back .fp-turn{--bs-btn-color:#7dd3fc;--bs-btn-border-color:#7dd3fc;--bs-btn-hover-color:var(--fp-back);--bs-btn-hover-bg:#7dd3fc;--bs-btn-hover-border-color:#7dd3fc;--bs-btn-active-color:var(--fp-back);--bs-btn-active-bg:#7dd3fc;--bs-btn-active-border-color:#7dd3fc;align-self:flex-start}
.fp-back .fp-turn:focus-visible{outline-color:#7dd3fc}
/* the turn, once the script has marked the card ready */
.fp .fp-turn{display:none}
.fp.is-ready .fp-turn{display:inline-flex}
.fp.is-ready .fp-inner{gap:0;transform-style:preserve-3d;transition:transform .7s cubic-bezier(.2,.7,.2,1)}
.fp.is-ready .fp-face{grid-area:1/1;-webkit-backface-visibility:hidden;backface-visibility:hidden}
.fp.is-ready .fp-back{transform:rotateY(180deg)}
.fp.is-flipped .fp-inner{transform:rotateY(180deg)}
.fp-x.is-ready .fp-back{transform:rotateX(180deg)}
.fp-x.is-flipped .fp-inner{transform:rotateX(180deg)}
/* colour themes */
.fp.t-rose{--fp-accent:#be123c;--fp-cover:linear-gradient(120deg,#fb7185,#f59e0b)}
.fp.t-teal{--fp-accent:#0f766e;--fp-cover:linear-gradient(120deg,#2dd4bf,#3b82f6)}
/* landscape speaker card */
.fp-x .fp-front.card{display:grid;grid-template-columns:auto 1fr;align-items:center;align-content:center;gap:6px 22px;padding:24px;text-align:left}
.fp-x .fp-av{grid-row:span 4;margin:0;border:0;width:104px;height:104px;border-radius:26px}
.fp-x .fp-body{display:contents}
.fp-x .fp-name{font-size:1.25rem}
.fp-talk{grid-column:2;font-size:.95rem;font-weight:600;line-height:1.4}
.fp-x .fp-front .fp-turn{grid-column:2;justify-self:start;margin-top:8px}
.fp-x .fp-back.card{display:grid;grid-template-columns:1fr auto;align-items:end;gap:16px 24px;padding:24px}
.fp-x .fp-facts{margin-top:12px}
.fp-x .fp-links{margin:0;flex-direction:column}
.fp-x .fp-back .fp-turn{grid-column:1/-1;justify-self:start}
@media (max-width:520px){.fp-x .fp-front.card,.fp-x .fp-back.card{grid-template-columns:1fr}.fp-x .fp-av{grid-row:auto}.fp-x .fp-front>*,.fp-talk,.fp-x .fp-front .fp-turn{grid-column:1}}
@media (prefers-reduced-motion:reduce){
  .fp .fp-turn{transition:none}
  .fp.is-ready .fp-inner,.fp.is-flipped .fp-inner{transform:none;transition:none}
  .fp.is-ready .fp-face{-webkit-backface-visibility:visible;backface-visibility:visible;transform:none;transition:opacity .25s,visibility 0s .25s}
  .fp.is-ready:not(.is-flipped) .fp-front,.fp.is-flipped .fp-back{transition:opacity .25s,visibility 0s}
  .fp.is-ready .fp-back,.fp.is-flipped .fp-front{opacity:0;visibility:hidden}
  .fp.is-flipped .fp-back{opacity:1;visibility:visible}
}
JavaScript
/* Flip cards: each .fp-turn turns its card over. The face that is turned away gets the inert attribute (no focus, hidden from
   screen readers). Turning to the back moves focus to the back face so it is read from the top; turning to the front (with its
   button or Escape) returns focus to the button that opened it. */
document.querySelectorAll('.fp').forEach(function (card) {
  var front = card.querySelector('.fp-front'), back = card.querySelector('.fp-back');
  var opener = front.querySelector('.fp-turn');
  function show(flipped, moveFocus) {
    card.classList.toggle('is-flipped', flipped);
    front.inert = flipped; back.inert = !flipped;
    opener.setAttribute('aria-expanded', String(flipped));
    if (moveFocus) (flipped ? back : opener).focus({ preventScroll: true });
  }
  card.querySelectorAll('.fp-turn').forEach(function (btn) {
    btn.addEventListener('click', function () { show(!card.classList.contains('is-flipped'), true); });
  });
  back.addEventListener('keydown', function (e) { if (e.key === 'Escape') show(false, true); });
  card.classList.add('is-ready');
  show(false, false);
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#475569] uppercase">Portrait cards that turn on the Y axis</span>
<ul class="grid grid-cols-[repeat(auto-fit,minmax(min(100%,250px),1fr))] gap-6">
  <li><article class="fp group h-full text-[#0f172a] perspective-[1400px]" aria-labelledby="fp-ines">
    <div class="fp-inner grid h-full gap-4">
      <div class="fp-face fp-front relative flex flex-col overflow-hidden rounded-[22px] border border-[#e2e8f0] bg-white text-center shadow-[0_24px_40px_-30px_rgba(15,23,42,.45)]">
        <div class="h-[86px] bg-[linear-gradient(120deg,#fb7185,#f59e0b)]" aria-hidden="true"></div>
        <span class="mx-auto -mt-12 block size-24 box-content overflow-hidden rounded-full border-4 border-white"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false" class="block size-full"><rect width="80" height="80" fill="#ffe4e6"/><path d="M22.5 39c0-14 7.6-22 17.5-22s17.5 8 17.5 22v23h-35z" fill="#7a3b1d"/><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#be123c"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#e9b48f"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#e9b48f"/><path d="M25 35.5c1-10.2 7-16.5 15-16.5s14.4 6.3 15 16.5c-6.4-1-12-5-15-10.4-3 5.4-8.6 9.4-15 10.4z" fill="#7a3b1d"/><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/></svg></span>
        <div class="flex flex-1 flex-col items-center px-[22px] pt-3 pb-[22px]">
          <h3 class="text-[1.15rem] font-extrabold tracking-[-.01em]" id="fp-ines">Inés Duarte</h3>
          <p class="mt-1 text-[.9rem] text-[#475569]">Picture book illustrator</p>
          <ul class="mt-4 mb-5 flex flex-wrap justify-center gap-1.5" aria-label="Specialities"><li class="rounded-full bg-[#f1f5f9] px-2.5 py-1 text-[.78rem] font-semibold text-[#334155]">Gouache</li><li class="rounded-full bg-[#f1f5f9] px-2.5 py-1 text-[.78rem] font-semibold text-[#334155]">Editorial</li><li class="rounded-full bg-[#f1f5f9] px-2.5 py-1 text-[.78rem] font-semibold text-[#334155]">Lettering</li></ul>
          <button class="fp-turn mt-auto items-center justify-center gap-2 rounded-xl border-[1.5px] border-[#be123c] px-4 py-2.5 text-[.88rem] font-bold text-[#be123c] transition-[color,background-color,border-color] duration-200 hover:bg-[#be123c] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#be123c] motion-reduce:transition-none cursor-pointer" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-4"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Read bio<span class="sr-only"> of Inés Duarte</span></button>
        </div>
      </div>
      <div class="fp-face fp-back relative flex flex-col overflow-hidden rounded-[22px] bg-[#0f172a] px-[22px] pt-6 pb-[22px] text-left text-[#e2e8f0] shadow-[0_24px_40px_-26px_rgba(15,23,42,.6)] focus:outline-none focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#0369a1]" tabindex="-1">
        <p class="text-[.72rem] font-bold tracking-[.12em] text-[#a5b4c8] uppercase">About</p>
        <p class="mt-1.5 text-[1.15rem] font-extrabold tracking-[-.01em] text-white">Inés Duarte</p>
        <p class="mt-2.5 text-[.9rem] leading-[1.55]">Paints covers for picture books and the odd newspaper op-ed. Sketches on paper, finishes in gouache, scans at 1200 dpi.</p>
        <dl class="mt-4 grid grid-cols-[auto_1fr] gap-x-3.5 gap-y-1.5 text-[.84rem]"><dt class="text-[#a5b4c8]">Based in</dt><dd class="font-semibold">Porto, Portugal</dd><dt class="text-[#a5b4c8]">Books</dt><dd class="font-semibold">23 published</dd><dt class="text-[#a5b4c8]">Replies</dt><dd class="font-semibold">Within two days</dd></dl>
        <ul class="my-5 flex gap-2"><li><a class="inline-flex items-center gap-1.5 rounded-[9px] bg-white/8 px-[11px] py-[7px] text-[.82rem] font-semibold text-white no-underline hover:bg-white/16 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#7dd3fc]" href="mailto:[email protected]"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-[15px]"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="M3.5 6.5l8.5 6.5 8.5-6.5"/></svg>Email</a></li><li><a class="inline-flex items-center gap-1.5 rounded-[9px] bg-white/8 px-[11px] py-[7px] text-[.82rem] font-semibold text-white no-underline hover:bg-white/16 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#7dd3fc]" href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-[15px]"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.6 3.8 5.6 3.8 9s-1.3 6.4-3.8 9c-2.5-2.6-3.8-5.6-3.8-9S9.5 5.6 12 3z"/></svg>Portfolio</a></li></ul>
        <button class="fp-turn mt-auto items-center justify-center gap-2 self-start rounded-xl border-[1.5px] border-[#7dd3fc] px-4 py-2.5 text-[.88rem] font-bold text-[#7dd3fc] transition-[color,background-color,border-color] duration-200 hover:bg-[#7dd3fc] hover:text-[#0f172a] focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#7dd3fc] motion-reduce:transition-none cursor-pointer" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-4"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to front<span class="sr-only"> of Inés Duarte's card</span></button>
      </div>
    </div>
  </article></li>
  <li><article class="fp group h-full text-[#0f172a] perspective-[1400px]" aria-labelledby="fp-ravi">
    <div class="fp-inner grid h-full gap-4">
      <div class="fp-face fp-front relative flex flex-col overflow-hidden rounded-[22px] border border-[#e2e8f0] bg-white text-center shadow-[0_24px_40px_-30px_rgba(15,23,42,.45)]">
        <div class="h-[86px] bg-[linear-gradient(120deg,#38bdf8,#6366f1)]" aria-hidden="true"></div>
        <span class="mx-auto -mt-12 block size-24 box-content overflow-hidden rounded-full border-4 border-white"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false" class="block size-full"><rect width="80" height="80" fill="#e0f2fe"/><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#0369a1"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#a86b48"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#a86b48"/><path d="M24.7 35.5c-.8-10.6 6-17.3 15.3-17.3 9.6 0 16 7 15.3 17.3-9.4 0-15-3.4-17.4-8.4-2.4 4.6-6.6 7.6-13.2 8.4z" fill="#1c1310"/><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><g fill="none" stroke="#1f1a17" stroke-width="1.5"><circle cx="34.5" cy="38.5" r="4.4"/><circle cx="45.5" cy="38.5" r="4.4"/><path d="M38.9 38.2h2.2"/></g></svg></span>
        <div class="flex flex-1 flex-col items-center px-[22px] pt-3 pb-[22px]">
          <h3 class="text-[1.15rem] font-extrabold tracking-[-.01em]" id="fp-ravi">Ravi Menon</h3>
          <p class="mt-1 text-[.9rem] text-[#475569]">Backend engineer, payments</p>
          <ul class="mt-4 mb-5 flex flex-wrap justify-center gap-1.5" aria-label="Specialities"><li class="rounded-full bg-[#f1f5f9] px-2.5 py-1 text-[.78rem] font-semibold text-[#334155]">Go</li><li class="rounded-full bg-[#f1f5f9] px-2.5 py-1 text-[.78rem] font-semibold text-[#334155]">Postgres</li><li class="rounded-full bg-[#f1f5f9] px-2.5 py-1 text-[.78rem] font-semibold text-[#334155]">Kafka</li></ul>
          <button class="fp-turn mt-auto items-center justify-center gap-2 rounded-xl border-[1.5px] border-[#0369a1] px-4 py-2.5 text-[.88rem] font-bold text-[#0369a1] transition-[color,background-color,border-color] duration-200 hover:bg-[#0369a1] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#0369a1] motion-reduce:transition-none cursor-pointer" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-4"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Read bio<span class="sr-only"> of Ravi Menon</span></button>
        </div>
      </div>
      <div class="fp-face fp-back relative flex flex-col overflow-hidden rounded-[22px] bg-[#0f172a] px-[22px] pt-6 pb-[22px] text-left text-[#e2e8f0] shadow-[0_24px_40px_-26px_rgba(15,23,42,.6)] focus:outline-none focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#0369a1]" tabindex="-1">
        <p class="text-[.72rem] font-bold tracking-[.12em] text-[#a5b4c8] uppercase">About</p>
        <p class="mt-1.5 text-[1.15rem] font-extrabold tracking-[-.01em] text-white">Ravi Menon</p>
        <p class="mt-2.5 text-[.9rem] leading-[1.55]">Moves money between 31 currencies without losing a cent. Writes the incident reviews people actually read.</p>
        <dl class="mt-4 grid grid-cols-[auto_1fr] gap-x-3.5 gap-y-1.5 text-[.84rem]"><dt class="text-[#a5b4c8]">Based in</dt><dd class="font-semibold">Kochi, India</dd><dt class="text-[#a5b4c8]">On call</dt><dd class="font-semibold">Week 2 of 6</dd><dt class="text-[#a5b4c8]">Talks</dt><dd class="font-semibold">4 this year</dd></dl>
        <ul class="my-5 flex gap-2"><li><a class="inline-flex items-center gap-1.5 rounded-[9px] bg-white/8 px-[11px] py-[7px] text-[.82rem] font-semibold text-white no-underline hover:bg-white/16 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#7dd3fc]" href="mailto:[email protected]"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-[15px]"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="M3.5 6.5l8.5 6.5 8.5-6.5"/></svg>Email</a></li><li><a class="inline-flex items-center gap-1.5 rounded-[9px] bg-white/8 px-[11px] py-[7px] text-[.82rem] font-semibold text-white no-underline hover:bg-white/16 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#7dd3fc]" href="#"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false" class="size-[15px]"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>GitHub</a></li></ul>
        <button class="fp-turn mt-auto items-center justify-center gap-2 self-start rounded-xl border-[1.5px] border-[#7dd3fc] px-4 py-2.5 text-[.88rem] font-bold text-[#7dd3fc] transition-[color,background-color,border-color] duration-200 hover:bg-[#7dd3fc] hover:text-[#0f172a] focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#7dd3fc] motion-reduce:transition-none cursor-pointer" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-4"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to front<span class="sr-only"> of Ravi Menon's card</span></button>
      </div>
    </div>
  </article></li>
  <li><article class="fp group h-full text-[#0f172a] perspective-[1400px]" aria-labelledby="fp-freya">
    <div class="fp-inner grid h-full gap-4">
      <div class="fp-face fp-front relative flex flex-col overflow-hidden rounded-[22px] border border-[#e2e8f0] bg-white text-center shadow-[0_24px_40px_-30px_rgba(15,23,42,.45)]">
        <div class="h-[86px] bg-[linear-gradient(120deg,#2dd4bf,#3b82f6)]" aria-hidden="true"></div>
        <span class="mx-auto -mt-12 block size-24 box-content overflow-hidden rounded-full border-4 border-white"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false" class="block size-full"><rect width="80" height="80" fill="#ccfbf1"/><circle cx="40" cy="17.5" r="7.5" fill="#c9a063"/><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#0f766e"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#f3d2b3"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#f3d2b3"/><path d="M25 36.5c0-10.4 6.6-17 15-17s15 6.6 15 17c-4.4-3.4-9.6-8-15-8s-10.6 4.6-15 8z" fill="#c9a063"/><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/></svg></span>
        <div class="flex flex-1 flex-col items-center px-[22px] pt-3 pb-[22px]">
          <h3 class="text-[1.15rem] font-extrabold tracking-[-.01em]" id="fp-freya">Freya Lindahl</h3>
          <p class="mt-1 text-[.9rem] text-[#475569]">User researcher</p>
          <ul class="mt-4 mb-5 flex flex-wrap justify-center gap-1.5" aria-label="Specialities"><li class="rounded-full bg-[#f1f5f9] px-2.5 py-1 text-[.78rem] font-semibold text-[#334155]">Interviews</li><li class="rounded-full bg-[#f1f5f9] px-2.5 py-1 text-[.78rem] font-semibold text-[#334155]">Diary studies</li><li class="rounded-full bg-[#f1f5f9] px-2.5 py-1 text-[.78rem] font-semibold text-[#334155]">Surveys</li></ul>
          <button class="fp-turn mt-auto items-center justify-center gap-2 rounded-xl border-[1.5px] border-[#0f766e] px-4 py-2.5 text-[.88rem] font-bold text-[#0f766e] transition-[color,background-color,border-color] duration-200 hover:bg-[#0f766e] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#0f766e] motion-reduce:transition-none cursor-pointer" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-4"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Read bio<span class="sr-only"> of Freya Lindahl</span></button>
        </div>
      </div>
      <div class="fp-face fp-back relative flex flex-col overflow-hidden rounded-[22px] bg-[#0f172a] px-[22px] pt-6 pb-[22px] text-left text-[#e2e8f0] shadow-[0_24px_40px_-26px_rgba(15,23,42,.6)] focus:outline-none focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#0369a1]" tabindex="-1">
        <p class="text-[.72rem] font-bold tracking-[.12em] text-[#a5b4c8] uppercase">About</p>
        <p class="mt-1.5 text-[1.15rem] font-extrabold tracking-[-.01em] text-white">Freya Lindahl</p>
        <p class="mt-2.5 text-[.9rem] leading-[1.55]">Talks to six customers every week and turns the notes into decisions. Keeps a public log of everything she got wrong.</p>
        <dl class="mt-4 grid grid-cols-[auto_1fr] gap-x-3.5 gap-y-1.5 text-[.84rem]"><dt class="text-[#a5b4c8]">Based in</dt><dd class="font-semibold">Malmö, Sweden</dd><dt class="text-[#a5b4c8]">Interviews</dt><dd class="font-semibold">412 so far</dd><dt class="text-[#a5b4c8]">Languages</dt><dd class="font-semibold">Swedish, English</dd></dl>
        <ul class="my-5 flex gap-2"><li><a class="inline-flex items-center gap-1.5 rounded-[9px] bg-white/8 px-[11px] py-[7px] text-[.82rem] font-semibold text-white no-underline hover:bg-white/16 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#7dd3fc]" href="mailto:[email protected]"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-[15px]"><rect x="3" y="5" width="18" height="14" rx="2.5"/><path d="M3.5 6.5l8.5 6.5 8.5-6.5"/></svg>Email</a></li><li><a class="inline-flex items-center gap-1.5 rounded-[9px] bg-white/8 px-[11px] py-[7px] text-[.82rem] font-semibold text-white no-underline hover:bg-white/16 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#7dd3fc]" href="#"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false" class="size-[15px]"><path d="M4.98 3.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5zM3 9.75h4V21H3zM9.5 9.75h3.8v1.6h.06c.53-1 1.83-2.05 3.77-2.05 4.03 0 4.77 2.6 4.77 6V21h-4v-5c0-1.2-.02-2.75-1.68-2.75-1.68 0-1.94 1.3-1.94 2.66V21h-4z"/></svg>LinkedIn</a></li></ul>
        <button class="fp-turn mt-auto items-center justify-center gap-2 self-start rounded-xl border-[1.5px] border-[#7dd3fc] px-4 py-2.5 text-[.88rem] font-bold text-[#7dd3fc] transition-[color,background-color,border-color] duration-200 hover:bg-[#7dd3fc] hover:text-[#0f172a] focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#7dd3fc] motion-reduce:transition-none cursor-pointer" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-4"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to front<span class="sr-only"> of Freya Lindahl's card</span></button>
      </div>
    </div>
  </article></li>
</ul></div>

<div class="max-w-[620px]"><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#475569] uppercase">Landscape speaker card that turns on the X axis</span>
<article class="fp fp-x group h-full text-[#0f172a] perspective-[1400px]" aria-labelledby="fp-selin">
  <div class="fp-inner grid h-full gap-4">
    <div class="fp-face fp-front relative grid grid-cols-[auto_1fr] content-center items-center gap-x-[22px] gap-y-1.5 overflow-hidden rounded-[22px] border border-[#e2e8f0] bg-white p-6 text-left shadow-[0_24px_40px_-30px_rgba(15,23,42,.45)] max-[520px]:grid-cols-1">
      <span class="row-span-4 block size-[104px] overflow-hidden rounded-[26px] max-[520px]:row-auto"><svg viewBox="0 0 80 80" width="80" height="80" aria-hidden="true" focusable="false" class="block size-full"><rect width="80" height="80" fill="#ede9fe"/><g fill="#2a1a12"><circle cx="26" cy="38" r="6"/><circle cx="54" cy="38" r="6"/></g><path d="M12 80c1-14 12-22 28-22s27 8 28 22z" fill="#7c3aed"/><path d="M34.5 49h11v8.5c-2.5 2.6-8.5 2.6-11 0z" fill="#c98b62"/><path d="M34.5 53.5c3.6 1.6 7.4 1.6 11 0v-3h-11z" fill="#000" opacity=".12"/><circle cx="40" cy="37" r="15" fill="#c98b62"/><g fill="#2a1a12"><circle cx="27.5" cy="30" r="6.5"/><circle cx="33.5" cy="23.5" r="7"/><circle cx="41" cy="21.5" r="7.5"/><circle cx="48.5" cy="24" r="7"/><circle cx="53" cy="31" r="6"/></g><circle cx="34.5" cy="38.5" r="1.7" fill="#1f1a17"/><circle cx="45.5" cy="38.5" r="1.7" fill="#1f1a17"/><path d="M35.8 44.6q4.2 3.2 8.4 0" stroke="#1f1a17" stroke-width="1.7" fill="none" stroke-linecap="round"/><circle cx="31" cy="43" r="2.4" fill="#e8746a" opacity=".35"/><circle cx="49" cy="43" r="2.4" fill="#e8746a" opacity=".35"/></svg></span>
      <h3 class="text-[1.25rem] font-extrabold tracking-[-.01em]" id="fp-selin">Selin Aydın</h3>
      <p class="text-[.9rem] text-[#475569]">Staff designer, incident tooling</p>
      <p class="text-[.95rem] leading-[1.4] font-semibold">Designing for 3 a.m.: interfaces for people who just got paged</p>
      <button class="fp-turn mt-2 cursor-pointer items-center justify-center gap-2 justify-self-start rounded-xl border-[1.5px] border-[#0369a1] px-4 py-2.5 text-[.88rem] font-bold text-[#0369a1] transition-[color,background-color,border-color] duration-200 hover:bg-[#0369a1] hover:text-white focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#0369a1] motion-reduce:transition-none" type="button" aria-expanded="false"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-4"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Session details<span class="sr-only"> for Selin Aydın</span></button>
    </div>
    <div class="fp-face fp-back relative grid grid-cols-[1fr_auto] items-end gap-x-6 gap-y-4 overflow-hidden rounded-[22px] bg-[#0f172a] p-6 text-left text-[#e2e8f0] shadow-[0_24px_40px_-26px_rgba(15,23,42,.6)] focus:outline-none focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#0369a1] max-[520px]:grid-cols-1" tabindex="-1">
      <div>
        <p class="text-[.72rem] font-bold tracking-[.12em] text-[#a5b4c8] uppercase">Session</p>
        <p class="mt-1.5 text-[1.25rem] font-extrabold tracking-[-.01em] text-white">Designing for 3 a.m.</p>
        <dl class="mt-3 grid grid-cols-[auto_1fr] gap-x-3.5 gap-y-1.5 text-[.84rem]"><dt class="text-[#a5b4c8]">When</dt><dd class="font-semibold">Day 2, 14:30 to 15:10</dd><dt class="text-[#a5b4c8]">Where</dt><dd class="font-semibold">Room B, the Atrium</dd><dt class="text-[#a5b4c8]">Track</dt><dd class="font-semibold">Reliability</dd></dl>
      </div>
      <ul class="flex flex-col items-start gap-2"><li><a class="inline-flex items-center gap-1.5 rounded-[9px] bg-white/8 px-[11px] py-[7px] text-[.82rem] font-semibold text-white no-underline hover:bg-white/16 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#7dd3fc]" href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-[15px]"><rect x="3.5" y="5" width="17" height="15.5" rx="2.5"/><path d="M3.5 10h17M8 3v4M16 3v4"/></svg>Add to calendar</a></li><li><a class="inline-flex items-center gap-1.5 rounded-[9px] bg-white/8 px-[11px] py-[7px] text-[.82rem] font-semibold text-white no-underline hover:bg-white/16 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#7dd3fc]" href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-[15px]"><path d="M10 14a4.5 4.5 0 0 0 6.4 0l3-3a4.5 4.5 0 0 0-6.4-6.4l-1.2 1.2"/><path d="M14 10a4.5 4.5 0 0 0-6.4 0l-3 3a4.5 4.5 0 0 0 6.4 6.4l1.2-1.2"/></svg>Slides</a></li></ul>
      <button class="fp-turn col-span-full cursor-pointer items-center justify-center gap-2 justify-self-start rounded-xl border-[1.5px] border-[#7dd3fc] px-4 py-2.5 text-[.88rem] font-bold text-[#7dd3fc] transition-[color,background-color,border-color] duration-200 hover:bg-[#7dd3fc] hover:text-[#0f172a] focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-[#7dd3fc] motion-reduce:transition-none" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false" class="size-4"><path d="M4 12a8 8 0 0 1 13.7-5.6L20 8.5M20 4v4.5h-4.5M20 12a8 8 0 0 1-13.7 5.6L4 15.5M4 20v-4.5h4.5"/></svg>Back to speaker<span class="sr-only"> Selin Aydın</span></button>
    </div>
  </div>
</article></div>
CSS
/* The turn itself: once the script adds .is-ready the faces share one grid cell and hide their backs; .is-flipped rotates
   the card (Y axis, or X axis with .fp-x). Under prefers-reduced-motion the turn becomes a crossfade. */
.fp-turn{display:none}
.fp.is-ready .fp-turn{display:inline-flex}
.fp.is-ready .fp-inner{gap:0;transform-style:preserve-3d;transition:transform .7s cubic-bezier(.2,.7,.2,1)}
.fp.is-ready .fp-face{grid-area:1/1;-webkit-backface-visibility:hidden;backface-visibility:hidden}
.fp.is-ready .fp-back{transform:rotateY(180deg)}
.fp.is-flipped .fp-inner{transform:rotateY(180deg)}
.fp-x.is-ready .fp-back{transform:rotateX(180deg)}
.fp-x.is-flipped .fp-inner{transform:rotateX(180deg)}
@media (prefers-reduced-motion:reduce){
  .fp.is-ready .fp-inner,.fp.is-flipped .fp-inner{transform:none;transition:none}
  .fp.is-ready .fp-face{-webkit-backface-visibility:visible;backface-visibility:visible;transform:none;transition:opacity .25s,visibility 0s .25s}
  .fp.is-ready:not(.is-flipped) .fp-front,.fp.is-flipped .fp-back{transition:opacity .25s,visibility 0s}
  .fp.is-ready .fp-back,.fp.is-flipped .fp-front{opacity:0;visibility:hidden}
  .fp.is-flipped .fp-back{opacity:1;visibility:visible}
}
JavaScript
/* Flip cards: each .fp-turn turns its card over. The face that is turned away gets the inert attribute (no focus, hidden from
   screen readers). Turning to the back moves focus to the back face so it is read from the top; turning to the front (with its
   button or Escape) returns focus to the button that opened it. */
document.querySelectorAll('.fp').forEach(function (card) {
  var front = card.querySelector('.fp-front'), back = card.querySelector('.fp-back');
  var opener = front.querySelector('.fp-turn');
  function show(flipped, moveFocus) {
    card.classList.toggle('is-flipped', flipped);
    front.inert = flipped; back.inert = !flipped;
    opener.setAttribute('aria-expanded', String(flipped));
    if (moveFocus) (flipped ? back : opener).focus({ preventScroll: true });
  }
  card.querySelectorAll('.fp-turn').forEach(function (btn) {
    btn.addEventListener('click', function () { show(!card.classList.contains('is-flipped'), true); });
  });
  back.addEventListener('keydown', function (e) { if (e.key === 'Escape') show(false, true); });
  card.classList.add('is-ready');
  show(false, false);
});

About this set

Profile cards with a second side for the longer story. The portrait cards that turn on the Y axis show an illustrated avatar, the name, the role and three speciality tags on the front, and a dark back with a bio, three facts and Email and Portfolio links. The landscape speaker card turns on the X axis, from a speaker’s name and talk title to the session time, room, track and links to the calendar entry and slides. Unlike hover flip cards, these turn only when a button is pressed, Read bio on the front and Back to front on the back, so they work on touch screens and with a keyboard. The two faces share one grid cell, so the card is as tall as its longer side and never clips the back. A short script makes the hidden face inert, moves focus to the back when it comes up, returns focus to Read bio when the card turns back, and lets Escape turn it back. Without JavaScript the faces simply stack. Under prefers-reduced-motion the turn becomes a quick crossfade.

What’s included

  • Turns on a button press, never on hover alone
  • Hidden face is inert, so focus never lands on invisible links
  • Escape on the back turns the card to the front
  • Crossfade instead of a 3D turn under reduced motion
  • Y-axis portrait cards and an X-axis landscape speaker card

Accessibility

The turn buttons are real buttons with the person's name in hidden text, and the front button reports aria-expanded. The face turned away carries the inert attribute, so it is skipped by Tab and by screen readers; the back face receives focus when it appears and Escape returns to the front. Buttons and links show a 3px focus outline, and reduced motion replaces the rotation with a fade.

Customise it

Each card reads --fp-accent and --fp-cover (the .t-rose and .t-teal classes are examples), and the back uses --fp-back, --fp-back-ink and --fp-back-muted. Change the 0.7s duration on .fp-inner for a faster turn. In Tailwind the turn rules sit in a short style block and colours are utilities.