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.
The record is sandwiched between the two sides of the sleeve. On hover or focus it slides out, its label starts to spin and the sleeve turns its open end to you.
Your edited version
translateZ(-3px), record at 0, front board at translateZ(3px). The record hides behind the front board with no z-index at all.translateX(70px). The whole set turns at the same time, so the open end swings toward you.paused; :hover only flips animation-play-state to running, so it stops where it is instead of snapping back.repeating-radial-gradient; the reflections are a conic-gradient on the same element that does not spin, like light on a real record.three planes at different translateZanimation-play-state toggled by :hoverconic + repeating-radial gradientsstatic hit area<div class="scene">
<div class="vinyl" tabindex="0">
<div class="set">
<i class="back"></i>
<i class="spine"></i>
<div class="disc"><i class="label"></i></div>
<div class="front"><b>NIGHT<br>DRIVE</b><span>Side A · 33⅓</span></div>
</div>
</div>
</div>
.scene {
perspective: 800px;
}
/* static hit area */
.vinyl {
display: grid;
place-items: center;
width: 216px;
height: 160px;
border-radius: 16px;
outline-offset: -4px;
cursor: pointer;
transform-style: preserve-3d;
}
.set {
position: relative;
width: 120px;
height: 120px;
pointer-events: none;
transform-style: preserve-3d;
transform: translateX(-8px) rotateX(4deg) rotateY(18deg);
transition: transform 0.7s cubic-bezier(0.3, 1.2, 0.5, 1);
}
.front,
.back {
position: absolute;
inset: 0;
border-radius: 3px;
}
.back {
background: #312a5d;
transform: translateZ(-3px);
}
.front {
display: flex;
flex-direction: column;
justify-content: space-between;
box-sizing: border-box;
padding: 10px;
overflow: hidden;
border: 1px solid rgb(255 255 255 / 0.22);
background:
radial-gradient(circle at 78% 72%, transparent 0 17px, rgb(255 255 255 / 0.9) 17.5px 19px, transparent 19.5px 27px, rgb(255 255 255 / 0.55) 27.5px 29px, transparent 29.5px 38px, rgb(255 255 255 / 0.3) 38.5px 40px, transparent 40.5px),
radial-gradient(circle at 78% 72%, #ffb547 0 17px, transparent 17.5px),
linear-gradient(150deg, #8b6cff, #ff4d9d 85%);
color: #fff;
font-family: system-ui, sans-serif;
backface-visibility: hidden;
transform: translateZ(3px);
}
.front b { font-size: 17px; font-weight: 900; line-height: 0.95; letter-spacing: -0.5px; }
.front span { font-size: 7px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; opacity: 0.85; }
/* closes the left side: centred on the left edge, turned side-on */
.spine {
position: absolute;
top: 0;
left: -3px;
width: 6px;
height: 100%;
background: #5846a3;
transform: rotateY(90deg);
}
.disc {
position: absolute;
inset: 6px;
display: grid;
place-items: center;
border-radius: 50%;
background:
conic-gradient(from 35deg, transparent 0 6%, rgb(255 255 255 / 0.22) 11%, transparent 17% 56%, rgb(255 255 255 / 0.16) 61%, transparent 67%),
repeating-radial-gradient(circle, #0c0c12 0 1.5px, #1f1f2a 1.5px 3px);
transform: translateX(12px);
transition: transform 0.7s cubic-bezier(0.3, 1.1, 0.5, 1);
}
.label {
width: 40px;
height: 40px;
border-radius: 50%;
background:
radial-gradient(circle, #0c0c12 0 2.5px, transparent 3px),
conic-gradient(#ffb547 0 25%, #ff4d9d 0 50%, #ffb547 0 75%, #ff4d9d 0);
animation: spin 1.8s linear infinite paused;
}
.vinyl:hover .set,
.vinyl:focus-visible .set {
transform: translateX(-36px) rotateX(6deg) rotateY(-22deg);
}
.vinyl:hover .disc,
.vinyl:focus-visible .disc {
transform: translateX(70px);
}
.vinyl:hover .label,
.vinyl:focus-visible .label {
animation-play-state: running;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
$s: 120px; // sleeve
$gap: 3px; // half the sleeve thickness
// Static hit area; the moving parts inside ignore the pointer.
.d-vinyl {
display: grid;
place-items: center;
width: 216px;
height: 160px;
border-radius: 16px;
outline-offset: -4px;
cursor: pointer;
transform-style: preserve-3d;
&__set {
position: relative;
width: $s;
height: $s;
pointer-events: none;
transform-style: preserve-3d;
transform: translateX(-8px) rotateX(4deg) rotateY(18deg);
transition: transform 0.7s cubic-bezier(0.3, 1.2, 0.5, 1);
}
// The sleeve is two boards with the record between them: a real sandwich in Z, so the
// record is hidden by the front board and hides the back one without any z-index.
&__front,
&__back {
position: absolute;
inset: 0;
border-radius: 3px;
}
&__back {
background: color-mix(in srgb, var(--accent) 30%, #0b0d18);
transform: translateZ(-$gap);
}
&__front {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 10px;
overflow: hidden;
border: 1px solid rgb(255 255 255 / 0.22);
background:
radial-gradient(circle at 78% 72%, transparent 0 17px, rgb(255 255 255 / 0.9) 17.5px 19px, transparent 19.5px 27px, rgb(255 255 255 / 0.55) 27.5px 29px, transparent 29.5px 38px, rgb(255 255 255 / 0.3) 38.5px 40px, transparent 40.5px),
radial-gradient(circle at 78% 72%, var(--warm) 0 17px, transparent 17.5px),
linear-gradient(150deg, var(--accent), var(--hot) 85%);
color: #fff;
backface-visibility: hidden;
transform: translateZ($gap);
b {
font-size: 17px;
font-weight: 900;
line-height: 0.95;
letter-spacing: -0.5px;
}
span {
font-size: 7px;
font-weight: 700;
letter-spacing: 1px;
text-transform: uppercase;
opacity: 0.85;
}
}
// closes the sleeve on the left; centred on the left edge and turned side-on
&__spine {
position: absolute;
top: 0;
left: -$gap;
width: $gap * 2;
height: 100%;
background: color-mix(in srgb, var(--accent) 60%, #0b0d18);
transform: rotateY(90deg);
}
&__disc {
position: absolute;
inset: 6px;
display: grid;
place-items: center;
border-radius: 50%;
// The grooves and the light streaks do NOT spin: on a real record the reflections stay
// put while the disc turns. Only the label below rotates.
background:
conic-gradient(from 35deg, transparent 0 6%, rgb(255 255 255 / 0.22) 11%, transparent 17% 56%, rgb(255 255 255 / 0.16) 61%, transparent 67%),
repeating-radial-gradient(circle, #0c0c12 0 1.5px, #1f1f2a 1.5px 3px);
transform: translateX(12px);
transition: transform 0.7s cubic-bezier(0.3, 1.1, 0.5, 1);
}
&__label {
width: 40px;
height: 40px;
border-radius: 50%;
background:
radial-gradient(circle, #0c0c12 0 2.5px, transparent 3px),
conic-gradient(var(--warm) 0 25%, var(--hot) 0 50%, var(--warm) 0 75%, var(--hot) 0);
animation: d-vinyl-spin 1.8s linear infinite paused;
}
&:hover &__set,
&:focus-visible &__set {
transform: translateX(-36px) rotateX(6deg) rotateY(-22deg);
}
&:hover &__disc,
&:focus-visible &__disc {
transform: translateX(70px);
}
&:hover &__label,
&:focus-visible &__label {
animation-play-state: running;
}
}
@keyframes d-vinyl-spin {
to {
transform: rotate(360deg);
}
}