Payment card tilt
CSS + JSThe card leans toward your pointer and a glare follows it. JS writes four custom properties; CSS does the tilt, the glare and the return to rest.
A 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.
Your edited version
translateZ(±depth / 2). The back is turned with rotateY(180deg) and both hide their backface, so each is only drawn while it faces you.--u, so the phone — screen type included — is the same share of a gallery card, the editor and a recording canvas.alternate with ease-in-out: it lingers on the front view and on the back view, and it is seamless without matching start and end.front / back at ±depth/2walls inset by the corner radiusrounded slabs plug the cornersalternate keyframes<div class="scene">
<div class="phone">
<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="slab" style="--i:4"></i><i class="slab" style="--i:5"></i>
<i class="slab" style="--i:6"></i><i class="slab" style="--i:7"></i><i class="slab" style="--i:8"></i>
<i class="wall wall-l"></i><i class="wall wall-r"></i>
<i class="wall wall-t"></i><i class="wall wall-b"></i>
<div class="front">
<div class="screen">
<span class="notch"></span>
<strong>9:41</strong><small>Friday 18</small>
<div class="widget"><i></i><i></i><i></i></div>
<div class="dock"><i></i><i></i><i></i></div>
</div>
</div>
<div class="back"><i></i><b></b></div>
</div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the phone is the same share of a
card, the editor, a full screen and a recording canvas */
--u: 0.36vmin;
perspective: calc(800 * var(--u));
}
/* 84 × 168, 10 thick, 15 corner radius */
.phone {
--metal: #5a607f;
--shine: color-mix(in srgb, #eceefb 60%, var(--metal));
position: relative;
width: calc(84 * var(--u));
height: calc(168 * var(--u));
transform-style: preserve-3d;
animation: turn 7s ease-in-out infinite alternate;
}
/* nine rounded slabs, one unit apart, fill the body so the corners look solid */
.slab {
position: absolute;
inset: calc(0.5 * var(--u));
border-radius: calc(15 * var(--u));
background: var(--metal);
transform: translateZ(calc((var(--i) - 4) * var(--u)));
}
/* walls cover only the straight part of each edge: inset by the radius */
.wall {
position: absolute;
background: linear-gradient(90deg, var(--metal), var(--shine) 50%, var(--metal));
}
.wall-l,
.wall-r {
left: calc(50% - 5 * var(--u));
top: calc(15 * var(--u));
width: calc(10 * var(--u));
height: calc(138 * var(--u));
}
.wall-t,
.wall-b {
left: calc(15 * var(--u));
top: calc(50% - 5 * var(--u));
width: calc(54 * var(--u));
height: calc(10 * var(--u));
background: linear-gradient(var(--metal), var(--shine) 50%, var(--metal));
}
.wall-l { transform: rotateY(-90deg) translateZ(calc(42 * var(--u))); } /* half the width */
.wall-r { transform: rotateY(90deg) translateZ(calc(42 * var(--u))); }
.wall-t { transform: rotateX(90deg) translateZ(calc(84 * var(--u))); } /* half the height */
.wall-b { transform: rotateX(-90deg) translateZ(calc(84 * var(--u))); }
.front,
.back {
position: absolute;
inset: 0;
border-radius: calc(15 * var(--u));
backface-visibility: hidden;
}
.front {
padding: calc(4 * var(--u)); /* the bezel */
background: #05060c;
transform: translateZ(calc(5 * var(--u))); /* half the thickness */
}
.screen {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
height: 100%;
padding: calc(18 * var(--u)) calc(7 * var(--u)) calc(7 * var(--u));
overflow: hidden;
border-radius: calc(11 * var(--u));
background:
/* a wallpaper deep enough for the white clock and date: the bright #8b6cff to #ff4d9d, and the
amber glow over the clock, made them 2.6:1 */
radial-gradient(circle at 80% 8%, rgb(255 181 71 / 0.4), transparent 45%),
linear-gradient(165deg, #6a45f5, #d1206f 70%, #c96f00 120%);
color: #fff;
}
.screen strong {
font-size: calc(21 * var(--u));
font-weight: 800;
line-height: 1;
letter-spacing: calc(-0.5 * var(--u));
}
.screen small { margin-top: calc(2 * var(--u)); font-size: calc(8 * var(--u)); }
.notch {
position: absolute;
top: calc(5 * var(--u));
left: calc(50% - 12 * var(--u));
width: calc(24 * var(--u));
height: calc(6 * var(--u));
border-radius: calc(3 * var(--u));
background: #05060c;
}
.widget {
display: grid;
gap: calc(4 * var(--u));
box-sizing: border-box;
width: 100%;
margin-top: calc(12 * var(--u));
padding: calc(7 * var(--u));
border: calc(1 * var(--u)) solid rgb(255 255 255 / 0.35);
border-radius: calc(8 * var(--u));
background: rgb(255 255 255 / 0.18);
}
.widget i { height: calc(4 * var(--u)); border-radius: calc(2 * var(--u)); background: rgb(255 255 255 / 0.85); }
.widget i:nth-child(2) { width: 70%; opacity: 0.6; }
.widget i:nth-child(3) { width: 45%; opacity: 0.6; }
.dock {
display: flex;
gap: calc(5 * var(--u));
justify-content: center;
box-sizing: border-box;
width: 100%;
margin-top: auto;
padding: calc(5 * var(--u));
border-radius: calc(9 * var(--u));
background: rgb(255 255 255 / 0.2);
}
.dock i { width: calc(13 * var(--u)); height: calc(13 * var(--u)); border-radius: calc(4 * var(--u)); background: rgb(255 255 255 / 0.9); }
.dock i:nth-child(2) { background: #2ee6d6; }
.dock i:nth-child(3) { background: #05060c; }
.back {
/* an inner line plus a soft shade instead of a hard border: turning, the back passes
side-on, where a one-unit line breaks up; the shade survives */
box-shadow:
inset 0 0 0 calc(1 * var(--u)) rgb(236 238 251 / 0.25),
inset 0 0 calc(8 * var(--u)) rgb(236 238 251 / 0.1);
background:
linear-gradient(125deg, transparent 40%, rgb(255 255 255 / 0.14) 50%, transparent 60%),
linear-gradient(160deg, #5546a2, #26254f);
transform: rotateY(180deg) translateZ(calc(5 * var(--u)));
}
/* camera island with two lenses */
.back i {
position: absolute;
top: calc(9 * var(--u));
left: calc(9 * var(--u));
width: calc(30 * var(--u));
height: calc(30 * var(--u));
border-radius: calc(9 * var(--u));
background:
radial-gradient(circle at calc(9 * var(--u)) calc(9 * var(--u)), #4a5078 0 calc(2 * var(--u)), #05060c calc(2.5 * var(--u)) calc(6 * var(--u)), transparent calc(6.5 * var(--u))),
radial-gradient(circle at calc(21 * var(--u)) calc(21 * var(--u)), #4a5078 0 calc(2 * var(--u)), #05060c calc(2.5 * var(--u)) calc(6 * var(--u)), transparent calc(6.5 * var(--u))),
#2f2957;
}
/* logo */
.back b {
position: absolute;
top: calc(50% - 8 * var(--u));
left: calc(50% - 8 * var(--u));
box-sizing: border-box;
width: calc(16 * var(--u));
height: calc(16 * var(--u));
border: calc(3 * var(--u)) solid rgb(255 255 255 / 0.55);
border-radius: 50%;
}
/* 215° = 180° + 35°: the back, seen from the mirrored angle */
@keyframes turn {
from { transform: rotateX(-8deg) rotateY(-35deg); }
to { transform: rotateX(-8deg) rotateY(215deg); }
}