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
--u, tied to the canvas, so the sleeve, its type and the record's grooves are the same share of a gallery card, the editor and a recording canvas. The numbers below are those units.translateZ(-3 units), record at 0, front board at +3. The record hides behind the front board with no z-index at all.translateX(66 units). 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 1/3</span></div>
</div>
</div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the sleeve is the same share of a
card, the editor, a full screen and a recording canvas */
--u: 0.48vmin;
perspective: calc(800 * var(--u));
}
/* static hit area */
.vinyl {
display: grid;
place-items: center;
width: calc(216 * var(--u));
height: calc(160 * var(--u));
border-radius: calc(16 * var(--u));
outline-offset: calc(-4 * var(--u));
cursor: pointer;
transform-style: preserve-3d;
}
.set {
position: relative;
width: calc(120 * var(--u));
height: calc(120 * var(--u));
pointer-events: none;
transform-style: preserve-3d;
transform: translateX(calc(-8 * var(--u))) 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: calc(3 * var(--u));
}
.back {
background: #312a5d;
transform: translateZ(calc(-3 * var(--u)));
}
.front {
display: flex;
flex-direction: column;
justify-content: space-between;
box-sizing: border-box;
padding: calc(10 * var(--u));
overflow: hidden;
border: calc(1 * var(--u)) solid rgb(255 255 255 / 0.22);
background:
radial-gradient(circle at 78% 72%, transparent 0 calc(17 * var(--u)), rgb(255 255 255 / 0.9) calc(17.5 * var(--u)) calc(19 * var(--u)), transparent calc(19.5 * var(--u)) calc(27 * var(--u)), rgb(255 255 255 / 0.55) calc(27.5 * var(--u)) calc(29 * var(--u)), transparent calc(29.5 * var(--u)) calc(38 * var(--u)), rgb(255 255 255 / 0.3) calc(38.5 * var(--u)) calc(40 * var(--u)), transparent calc(40.5 * var(--u))),
radial-gradient(circle at 78% 72%, #ffb547 0 calc(17 * var(--u)), transparent calc(17.5 * var(--u))),
linear-gradient(150deg, #6a45f5, #d1206f 85%); /* deep enough for the white words */
color: #fff;
font-family: Inter, system-ui, sans-serif;
backface-visibility: hidden;
transform: translateZ(calc(3 * var(--u)));
}
.front b {
font-size: calc(17 * var(--u));
font-weight: 900;
line-height: 0.95;
letter-spacing: calc(-0.5 * var(--u));
}
.front span {
font-size: calc(8 * var(--u));
font-weight: 700;
letter-spacing: calc(1 * var(--u));
text-transform: uppercase; /* full white: at 0.85 on the sleeve it was 2.9:1 */
}
/* closes the left side: centred on the left edge, turned side-on */
.spine {
position: absolute;
top: 0;
left: calc(-3 * var(--u));
width: calc(6 * var(--u));
height: 100%;
background: #5846a3;
transform: rotateY(90deg);
}
.disc {
position: absolute;
inset: calc(6 * var(--u));
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 calc(1.5 * var(--u)), #1f1f2a calc(1.5 * var(--u)) calc(3 * var(--u)));
transform: translateX(calc(12 * var(--u)));
transition: transform 0.7s cubic-bezier(0.3, 1.1, 0.5, 1);
}
.label {
width: calc(40 * var(--u));
height: calc(40 * var(--u));
border-radius: 50%;
background:
radial-gradient(circle, #0c0c12 0 calc(2.5 * var(--u)), transparent calc(3 * var(--u))),
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(calc(-36 * var(--u))) rotateX(6deg) rotateY(-22deg);
}
.vinyl:hover .disc,
.vinyl:focus-visible .disc {
transform: translateX(calc(66 * var(--u)));
}
.vinyl:hover .label,
.vinyl:focus-visible .label {
animation-play-state: running;
}
@keyframes spin {
to { transform: rotate(360deg); }
}