Phone mockup
Pure CSSA phone with real thickness: screen, back, four thin walls and a stack of rounded slabs that fills the corners. It turns to show both sides.
A thick business card with painted edges. Hover or focus and it flips to show the back; the logo and the text are lifted off the paper with translateZ, so they shift against it as it turns.
Your edited version
translateZ(±2px), the back turned with rotateY(180deg). Both hide their backface, so each shows only while it faces you.preserve-3d, and the logo and the lines of text are lifted off it with translateZ(2–6px). As the card turns, they slide against the paper.transform. The flip is a plain transition with a small overshoot.pointer-events: none, so the flip cannot pull the card out from under the pointer.two faces at ±depth/2, back turned 180degrounded slabs + walls for the edgeembossing = children lifted with translateZflip and idle sway on separate elements<div class="scene">
<div class="bizcard" tabindex="0">
<div class="float">
<i class="shadow"></i>
<div class="card">
<i class="slab" style="--i:0"></i><i class="slab" style="--i:1"></i><i class="slab" style="--i:2"></i><i class="slab" style="--i:3"></i>
<i class="wall wall-t"></i><i class="wall wall-b"></i><i class="wall wall-l"></i><i class="wall wall-r"></i>
<div class="front">
<b class="logo"><i></i><i></i><i></i></b>
<strong>PRISM</strong><small>DESIGN STUDIO</small>
</div>
<div class="back">
<b></b>
<strong>Alex Morgan</strong><small>Creative director</small>
<span>alex@prism.studio</span><span>+1 555 0142</span><span>prism.studio</span>
</div>
</div>
</div>
</div>
</div>
.scene {
perspective: 800px;
}
/* the static hit area */
.bizcard {
--ink: #0f1020;
--edge: #ff4d9d;
--foil: #ffd291;
--foil-deep: #aa6c16;
display: grid;
place-items: center;
width: 220px;
height: 170px;
border-radius: 16px;
cursor: pointer;
font-family: system-ui, sans-serif;
transform-style: preserve-3d;
}
/* the tilt and a slow idle sway */
.float {
position: relative;
width: 176px;
height: 100px;
pointer-events: none;
transform-style: preserve-3d;
animation: float 6s ease-in-out infinite alternate;
}
.shadow {
position: absolute;
inset: 10px -6px -16px;
border-radius: 50%;
background: radial-gradient(closest-side, rgb(0 0 0 / 0.38), transparent);
transform: translateZ(-36px);
}
/* the flip, on its own element */
.card {
position: absolute;
inset: 0;
transform-style: preserve-3d;
transition: transform 0.9s cubic-bezier(0.35, 1.25, 0.45, 1);
}
.bizcard:hover .card,
.bizcard:focus-visible .card {
transform: rotateY(180deg);
}
/* 4px thick: four rounded slabs fill the corners of the edge... */
.slab {
position: absolute;
inset: 0.5px;
border-radius: 6px;
background: var(--edge);
transform: translateZ(calc((var(--i) - 1.5) * 1px));
}
/* ...four walls give the straight parts a lit surface (inset by the 6px radius) */
.wall {
position: absolute;
background: linear-gradient(90deg, #b3366e, var(--edge) 50%, #ff82ba);
}
.wall-t, .wall-b { left: 6px; top: calc(50% - 2px); width: 164px; height: 4px; }
.wall-l, .wall-r { top: 6px; left: calc(50% - 2px); width: 4px; height: 88px; }
.wall-t { transform: rotateX(90deg) translateZ(50px); }
.wall-b { transform: rotateX(-90deg) translateZ(50px); }
.wall-l { transform: rotateY(-90deg) translateZ(88px); }
.wall-r { transform: rotateY(90deg) translateZ(88px); }
/* each printed side is 3D itself, so its print can be lifted off it */
.front,
.back {
position: absolute;
inset: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
border-radius: 6px;
transform-style: preserve-3d;
backface-visibility: hidden;
}
.front > *,
.back > * {
backface-visibility: hidden;
}
.front {
align-items: center;
justify-content: center;
gap: 3px;
background:
radial-gradient(circle at 50% 38%, rgb(139 108 255 / 0.38), transparent 55%),
repeating-linear-gradient(135deg, rgb(255 255 255 / 0.035) 0 1px, transparent 1px 6px),
var(--ink);
transform: translateZ(2px);
}
.front strong {
margin-top: 6px;
color: var(--foil);
font-size: 13px;
font-weight: 800;
letter-spacing: 5px;
text-indent: 5px; /* balances the space after the last letter */
text-shadow: 0 1px 0 var(--foil-deep);
transform: translateZ(3px);
}
.front small {
color: rgb(255 255 255 / 0.6);
font-size: 5.5px;
font-weight: 600;
letter-spacing: 2.5px;
text-indent: 2.5px;
transform: translateZ(1.5px);
}
/* foil prism logo, lifted the most */
.logo {
position: relative;
width: 34px;
height: 30px;
transform-style: preserve-3d;
transform: translateZ(6px);
}
.logo i {
position: absolute;
clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
.logo i:nth-child(1) { inset: 0; background: linear-gradient(135deg, #fff6dc, var(--foil) 40%, var(--foil-deep)); }
.logo i:nth-child(2) { inset: 9px 7.5px 3px; background: var(--ink); }
.logo i:nth-child(3) {
top: 15px;
left: 30px;
width: 26px;
height: 9px;
clip-path: polygon(0 40%, 100% 0, 100% 100%, 0 60%); /* the spectrum fanning out */
background: linear-gradient(#ff4d9d 0 33%, #ffb547 0 66%, #2ee6d6 0);
}
.back {
justify-content: center;
padding: 12px 14px;
color: #fff;
background:
radial-gradient(circle at 100% 0%, rgb(255 181 71 / 0.55), transparent 50%),
linear-gradient(135deg, #8b6cff, #c55dcb);
transform: rotateY(180deg) translateZ(2px);
}
.back strong { font-size: 13px; font-weight: 800; line-height: 1; text-shadow: 0 1px 0 rgb(0 0 0 / 0.25); transform: translateZ(3px); }
.back small { margin: 3px 0 9px; font-size: 6.5px; font-weight: 600; letter-spacing: 1.5px; text-transform: uppercase; opacity: 0.85; transform: translateZ(2px); }
.back span {
display: flex;
align-items: center;
gap: 5px;
margin-top: 3px;
font-size: 6.5px;
font-weight: 500;
transform: translateZ(1.5px);
}
.back span::before {
content: '';
width: 4px;
height: 4px;
border-radius: 50%;
background: #fff;
box-shadow: 0 0 0 1.5px rgb(255 77 157 / 0.6);
}
/* the small mark in the corner */
.back b {
position: absolute;
top: 12px;
right: 14px;
width: 18px;
height: 16px;
clip-path: polygon(50% 0, 100% 100%, 0 100%);
background: linear-gradient(135deg, #fff, rgb(255 255 255 / 0.55));
transform: translateZ(4px);
}
@keyframes float {
from { transform: translateY(-3px) rotateX(16deg) rotateY(-20deg) rotateZ(-5deg); }
to { transform: translateY(3px) rotateX(10deg) rotateY(-8deg) rotateZ(-3deg); }
}
$w: 176px;
$h: 100px;
$d: 4px; // a thick, premium card stock
$r: 6px; // corner radius
// Static hit area: hovering it never moves it.
.d-businesscard {
--ink: #0f1020;
--edge: var(--hot);
--foil: color-mix(in srgb, var(--warm) 80%, #fff);
--foil-deep: color-mix(in srgb, var(--warm) 60%, #3a2200);
display: grid;
place-items: center;
width: 220px;
height: 170px;
border-radius: 16px;
outline-offset: -4px;
cursor: pointer;
transform-style: preserve-3d;
// the tilt, with a slow idle sway so it is never quite still
&__float {
position: relative;
width: $w;
height: $h;
pointer-events: none;
transform-style: preserve-3d;
animation: d-businesscard-float 6s ease-in-out infinite alternate;
}
&__shadow {
position: absolute;
inset: 10px -6px -16px;
border-radius: 50%;
background: radial-gradient(closest-side, rgb(0 0 0 / 0.38), transparent);
transform: translateZ(-36px);
}
// the flip lives on its own element, inside the sway: the two never fight over transform
&__card {
position: absolute;
inset: 0;
transform-style: preserve-3d;
transition: transform 0.9s cubic-bezier(0.35, 1.25, 0.45, 1);
}
// Four rounded slabs, 1px apart, fill the rounded corners of the edge...
&__slab {
position: absolute;
inset: 0.5px;
border-radius: $r;
background: var(--edge);
transform: translateZ(calc((var(--i) - 1.5) * 1px));
}
// ...and four flat walls give the straight part of the edge a clean, lit surface.
&__wall {
position: absolute;
background: linear-gradient(90deg, color-mix(in srgb, var(--edge) 70%, #000), var(--edge) 50%, color-mix(in srgb, var(--edge) 70%, #fff));
&--t,
&--b {
left: $r;
top: calc(50% - #{$d * 0.5});
width: $w - 2 * $r;
height: $d;
}
&--l,
&--r {
top: $r;
left: calc(50% - #{$d * 0.5});
width: $d;
height: $h - 2 * $r;
}
&--t {
transform: rotateX(90deg) translateZ($h * 0.5);
}
&--b {
transform: rotateX(-90deg) translateZ($h * 0.5);
}
&--l {
transform: rotateY(-90deg) translateZ($w * 0.5);
}
&--r {
transform: rotateY(90deg) translateZ($w * 0.5);
}
}
// --- the two printed sides ------------------------------------------------------------
// Each side is a preserve-3d plane, so what is printed on it can be lifted off it with
// translateZ: the embossing. Every piece hides its own backface (it is 3D, not flat paint).
&__front,
&__back {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
border-radius: $r;
transform-style: preserve-3d;
backface-visibility: hidden;
> * {
backface-visibility: hidden;
}
}
&__front {
align-items: center;
justify-content: center;
gap: 3px;
background:
radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--accent) 38%, transparent), transparent 55%),
repeating-linear-gradient(135deg, rgb(255 255 255 / 0.035) 0 1px, transparent 1px 6px),
var(--ink);
transform: translateZ($d * 0.5);
strong {
margin-top: 6px;
color: var(--foil);
font-size: 13px;
font-weight: 800;
letter-spacing: 5px;
text-indent: 5px; // balances the letter-spacing after the last letter
text-shadow: 0 1px 0 var(--foil-deep);
transform: translateZ(3px);
}
small {
color: rgb(255 255 255 / 0.6);
font-size: 5.5px;
font-weight: 600;
letter-spacing: 2.5px;
text-indent: 2.5px;
transform: translateZ(1.5px);
}
}
// foil prism: a gold triangle with a dark core, a light ray in and a spectrum out
&__logo {
position: relative;
width: 34px;
height: 30px;
transform-style: preserve-3d;
transform: translateZ(6px);
i {
position: absolute;
clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
i:nth-child(1) {
inset: 0;
background: linear-gradient(135deg, #fff6dc, var(--foil) 40%, var(--foil-deep));
}
i:nth-child(2) {
inset: 9px 7.5px 3px;
background: var(--ink);
}
i:nth-child(3) {
top: 15px;
left: 30px;
width: 26px;
height: 9px;
clip-path: polygon(0 40%, 100% 0, 100% 100%, 0 60%);
background: linear-gradient(var(--hot) 0 33%, var(--warm) 0 66%, var(--accent-2) 0);
}
}
&__back {
justify-content: center;
padding: 12px 14px;
color: #fff;
background:
radial-gradient(circle at 100% 0%, color-mix(in srgb, var(--warm) 55%, transparent), transparent 50%),
linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 45%, var(--hot)));
transform: rotateY(180deg) translateZ($d * 0.5);
strong {
font-size: 13px;
font-weight: 800;
line-height: 1;
text-shadow: 0 1px 0 rgb(0 0 0 / 0.25);
transform: translateZ(3px);
}
small {
margin: 3px 0 9px;
font-size: 6.5px;
font-weight: 600;
letter-spacing: 1.5px;
text-transform: uppercase;
opacity: 0.85;
transform: translateZ(2px);
}
span {
display: flex;
align-items: center;
gap: 5px;
margin-top: 3px;
font-size: 6.5px;
font-weight: 500;
transform: translateZ(1.5px);
// a little round bullet in the edge colour
&::before {
content: '';
width: 4px;
height: 4px;
border-radius: 50%;
background: #fff;
box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--hot) 60%, transparent);
}
}
// the small mark in the corner
b {
position: absolute;
top: 12px;
right: 14px;
width: 18px;
height: 16px;
clip-path: polygon(50% 0, 100% 100%, 0 100%);
background: linear-gradient(135deg, #fff, rgb(255 255 255 / 0.55));
transform: translateZ(4px);
}
}
&:hover &__card,
&:focus-visible &__card {
transform: rotateY(180deg);
}
}
@keyframes d-businesscard-float {
from {
transform: translateY(-3px) rotateX(16deg) rotateY(-20deg) rotateZ(-5deg);
}
to {
transform: translateY(3px) rotateX(10deg) rotateY(-8deg) rotateZ(-3deg);
}
}