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.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 {
perspective: 800px;
}
/* 84 × 168, 10px thick, 15px corner radius */
.phone {
--metal: #5a607f;
--shine: color-mix(in srgb, #eceefb 60%, var(--metal));
position: relative;
width: 84px;
height: 168px;
transform-style: preserve-3d;
animation: turn 7s ease-in-out infinite alternate;
}
/* nine rounded slabs, 1px apart, fill the body so the corners look solid */
.slab {
position: absolute;
inset: 0.5px;
border-radius: 15px;
background: var(--metal);
transform: translateZ(calc((var(--i) - 4) * 1px));
}
/* 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% - 5px);
top: 15px;
width: 10px;
height: 138px;
}
.wall-t,
.wall-b {
left: 15px;
top: calc(50% - 5px);
width: 54px;
height: 10px;
background: linear-gradient(var(--metal), var(--shine) 50%, var(--metal));
}
.wall-l { transform: rotateY(-90deg) translateZ(42px); } /* half the width */
.wall-r { transform: rotateY(90deg) translateZ(42px); }
.wall-t { transform: rotateX(90deg) translateZ(84px); } /* half the height */
.wall-b { transform: rotateX(-90deg) translateZ(84px); }
.front,
.back {
position: absolute;
inset: 0;
border-radius: 15px;
backface-visibility: hidden;
}
.front {
padding: 4px; /* the bezel */
background: #05060c;
transform: translateZ(5px); /* half the thickness */
}
.screen {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
height: 100%;
padding: 18px 7px 7px;
overflow: hidden;
border-radius: 11px;
background:
radial-gradient(circle at 80% 8%, rgb(255 181 71 / 0.8), transparent 45%),
linear-gradient(165deg, #8b6cff, #ff4d9d 70%, #ffb547 120%);
color: #fff;
}
.screen strong { font-size: 21px; font-weight: 800; line-height: 1; letter-spacing: -0.5px; }
.screen small { margin-top: 2px; font-size: 7px; opacity: 0.85; }
.notch {
position: absolute;
top: 5px;
left: calc(50% - 12px);
width: 24px;
height: 6px;
border-radius: 3px;
background: #05060c;
}
.widget {
display: grid;
gap: 4px;
box-sizing: border-box;
width: 100%;
margin-top: 12px;
padding: 7px;
border: 1px solid rgb(255 255 255 / 0.35);
border-radius: 8px;
background: rgb(255 255 255 / 0.18);
}
.widget i { height: 4px; border-radius: 2px; 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: 5px;
justify-content: center;
box-sizing: border-box;
width: 100%;
margin-top: auto;
padding: 5px;
border-radius: 9px;
background: rgb(255 255 255 / 0.2);
}
.dock i { width: 13px; height: 13px; border-radius: 4px; background: rgb(255 255 255 / 0.9); }
.dock i:nth-child(2) { background: #2ee6d6; }
.dock i:nth-child(3) { background: #05060c; }
.back {
border: 1px solid rgb(236 238 251 / 0.25);
background:
linear-gradient(125deg, transparent 40%, rgb(255 255 255 / 0.14) 50%, transparent 60%),
linear-gradient(160deg, #5546a2, #26254f);
transform: rotateY(180deg) translateZ(5px);
}
/* camera island with two lenses */
.back i {
position: absolute;
top: 8px;
left: 8px;
width: 30px;
height: 30px;
border-radius: 9px;
background:
radial-gradient(circle at 9px 9px, #4a5078 0 2px, #05060c 2.5px 6px, transparent 6.5px),
radial-gradient(circle at 21px 21px, #4a5078 0 2px, #05060c 2.5px 6px, transparent 6.5px),
#2f2957;
}
/* logo */
.back b {
position: absolute;
top: calc(50% - 8px);
left: calc(50% - 8px);
box-sizing: border-box;
width: 16px;
height: 16px;
border: 3px 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); }
}
$w: 84px;
$h: 168px;
$d: 10px; // thickness
$r: 15px; // corner radius
.d-phone {
--metal: color-mix(in srgb, var(--muted) 55%, var(--surface-solid));
position: relative;
width: $w;
height: $h;
transform-style: preserve-3d;
// -35° shows the screen, 215° (= 180° + 35°) shows the back from the mirrored angle.
// `alternate` + ease-in-out lingers on both poses and is seamless by construction.
animation: d-phone-turn 7s ease-in-out infinite alternate;
// Flat walls cannot follow a rounded corner. Nine rounded slabs, 1px apart, fill the body
// so the corners look solid from every angle.
&__slab {
position: absolute;
inset: 0.5px;
border-radius: $r;
background: var(--metal);
transform: translateZ(calc((var(--i) - 4) * 1px));
}
// The walls cover only the straight part of each edge (inset by the radius).
&__wall {
position: absolute;
background: linear-gradient(90deg, var(--metal), color-mix(in srgb, var(--text) 60%, var(--metal)) 50%, var(--metal));
&--l,
&--r {
left: calc(50% - #{$d * 0.5});
top: $r;
width: $d;
height: $h - 2 * $r;
}
&--t,
&--b {
left: $r;
top: calc(50% - #{$d * 0.5});
width: $w - 2 * $r;
height: $d;
background: linear-gradient(var(--metal), color-mix(in srgb, var(--text) 60%, var(--metal)) 50%, var(--metal));
}
&--l {
transform: rotateY(-90deg) translateZ($w * 0.5);
}
&--r {
transform: rotateY(90deg) translateZ($w * 0.5);
}
&--t {
transform: rotateX(90deg) translateZ($h * 0.5);
}
&--b {
transform: rotateX(-90deg) translateZ($h * 0.5);
}
}
&__front,
&__back {
position: absolute;
inset: 0;
border-radius: $r;
backface-visibility: hidden;
}
&__front {
padding: 4px; // the bezel
background: #05060c;
transform: translateZ($d * 0.5);
}
&__screen {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
padding: 18px 7px 7px;
overflow: hidden;
border-radius: $r - 4px;
background:
radial-gradient(circle at 80% 8%, color-mix(in srgb, var(--warm) 80%, transparent), transparent 45%),
linear-gradient(165deg, var(--accent), var(--hot) 70%, var(--warm) 120%);
color: #fff;
strong {
font-size: 21px;
font-weight: 800;
line-height: 1;
letter-spacing: -0.5px;
}
small {
margin-top: 2px;
font-size: 7px;
opacity: 0.85;
}
}
&__notch {
position: absolute;
top: 5px;
left: calc(50% - 12px);
width: 24px;
height: 6px;
border-radius: 3px;
background: #05060c;
}
&__widget {
display: grid;
gap: 4px;
width: 100%;
margin-top: 12px;
padding: 7px;
border: 1px solid rgb(255 255 255 / 0.35);
border-radius: 8px;
background: rgb(255 255 255 / 0.18);
i {
height: 4px;
border-radius: 2px;
background: rgb(255 255 255 / 0.85);
&:nth-child(2) {
width: 70%;
opacity: 0.6;
}
&:nth-child(3) {
width: 45%;
opacity: 0.6;
}
}
}
&__dock {
display: flex;
gap: 5px;
justify-content: center;
width: 100%;
margin-top: auto;
padding: 5px;
border-radius: 9px;
background: rgb(255 255 255 / 0.2);
i {
width: 13px;
height: 13px;
border-radius: 4px;
background: rgb(255 255 255 / 0.9);
&:nth-child(2) {
background: var(--accent-2);
}
&:nth-child(3) {
background: #05060c;
}
}
}
&__back {
border: 1px solid color-mix(in srgb, var(--text) 25%, transparent);
background:
linear-gradient(125deg, transparent 40%, rgb(255 255 255 / 0.14) 50%, transparent 60%),
linear-gradient(160deg, color-mix(in srgb, var(--accent) 55%, var(--surface-solid)), color-mix(in srgb, var(--accent) 15%, var(--surface-solid)));
transform: rotateY(180deg) translateZ($d * 0.5);
// camera island with two lenses
i {
position: absolute;
top: 8px;
left: 8px;
width: 30px;
height: 30px;
border-radius: 9px;
background:
radial-gradient(circle at 9px 9px, #4a5078 0 2px, #05060c 2.5px 6px, transparent 6.5px),
radial-gradient(circle at 21px 21px, #4a5078 0 2px, #05060c 2.5px 6px, transparent 6.5px),
color-mix(in srgb, var(--accent) 25%, #10121f);
}
// logo
b {
position: absolute;
top: calc(50% - 8px);
left: calc(50% - 8px);
width: 16px;
height: 16px;
border: 3px solid rgb(255 255 255 / 0.55);
border-radius: 50%;
}
}
}
@keyframes d-phone-turn {
from {
transform: rotateX(-8deg) rotateY(-35deg);
}
to {
transform: rotateX(-8deg) rotateY(215deg);
}
}