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 rangefinder camera rocking on the table: a box body, a lens barrel of sixteen strips around the Z axis and a glass disc whose reflection slides the other way as it turns.
Your edited version
rotateZ(i × 22.5deg), moved out by the radius, folded flat with rotateX(90deg) and slid forward by half its length so the barrel starts at the body.cos() of its angle, so the barrel is lit from the top left without any hand-painted gradient per strip.overflow: hidden. The reflection runs the rocking animation in reverse (same duration, same easing), so it seems to stay with the room while the camera turns.rotateX(90deg) and stacked (the button's four 1.5 units apart, the dial's two 3 units apart): from above they read as short cylinders.--u, tied to the canvas, so the camera is the same share of a gallery card, the editor and a recording canvas.barrel strips: rotateZ(a) translateY(−r) rotateX(90deg)shading from cos() of the strip anglereflection counter-moves in a clipped discstacked flat discs for knobs<div class="scene">
<div class="camera">
<i class="shadow"></i>
<div class="face front"><i class="window"></i><i class="window"></i><span class="name">OBSCURA</span></div>
<i class="face back"></i>
<i class="face left"></i>
<i class="face right"></i>
<i class="face top"></i>
<i class="face bottom"></i>
<div class="lens">
<i class="mount"></i>
<i class="strip" style="--i:0"></i>
<i class="strip" style="--i:1"></i>
<i class="strip" style="--i:2"></i>
<i class="strip" style="--i:3"></i>
<i class="strip" style="--i:4"></i>
<i class="strip" style="--i:5"></i>
<i class="strip" style="--i:6"></i>
<i class="strip" style="--i:7"></i>
<i class="strip" style="--i:8"></i>
<i class="strip" style="--i:9"></i>
<i class="strip" style="--i:10"></i>
<i class="strip" style="--i:11"></i>
<i class="strip" style="--i:12"></i>
<i class="strip" style="--i:13"></i>
<i class="strip" style="--i:14"></i>
<i class="strip" style="--i:15"></i>
<i class="glass"><em></em></i>
<i class="ring"></i>
</div>
<div class="flash"><i></i><i></i><i></i><i></i><i></i></div>
<i class="dial" style="--h:1"></i><i class="dial dial-top" style="--h:4"></i>
<i class="knob" style="--h:1"></i><i class="knob" style="--h:2.5"></i><i class="knob" style="--h:4"></i><i class="knob knob-top" style="--h:5.5"></i>
</div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the camera is the same share of a
card, the editor, a full screen and a recording canvas */
--u: 0.46vmin;
perspective: calc(800 * var(--u));
}
/* 144 × 88, 40 units deep */
.camera {
--silver: #d9dce8;
--silver-dark: #8a90a8;
--leather: #2d2847;
position: relative;
width: calc(144 * var(--u));
height: calc(88 * var(--u));
transform-style: preserve-3d;
animation: rock 7s ease-in-out infinite alternate;
}
.shadow {
position: absolute;
left: calc(-16 * var(--u));
top: calc(48 * var(--u));
width: calc(176 * var(--u));
height: calc(80 * var(--u));
border-radius: 50%;
background: radial-gradient(closest-side, rgb(0 0 0 / 0.42), transparent);
transform: rotateX(90deg);
}
/* --- the body --- */
.face {
position: absolute;
top: 0;
left: 0;
width: calc(144 * var(--u));
height: calc(88 * var(--u));
/* silver plate, a seam, leatherette with grain, a seam, silver plate */
background:
linear-gradient(transparent calc(24 * var(--u)), rgb(0 0 0 / 0.35) calc(24 * var(--u)) calc(25 * var(--u)), transparent calc(25 * var(--u)) calc(79 * var(--u)), rgb(255 255 255 / 0.5) calc(79 * var(--u)) calc(80 * var(--u)), transparent calc(80 * var(--u))),
radial-gradient(circle, rgb(255 255 255 / 0.07) calc(0.8 * var(--u)), transparent calc(1.2 * var(--u))) 0 0 / calc(3 * var(--u)) calc(3 * var(--u)),
linear-gradient(var(--silver) 0 calc(3 * var(--u)), #f4f5fa calc(8 * var(--u)), var(--silver) calc(18 * var(--u)), var(--silver-dark) calc(24 * var(--u)), var(--leather) calc(24 * var(--u)) calc(80 * var(--u)), var(--silver) calc(80 * var(--u)), var(--silver-dark));
}
.front { transform: translateZ(calc(20 * var(--u))); }
.back { transform: rotateY(180deg) translateZ(calc(20 * var(--u))); }
.left, .right { left: calc(52 * var(--u)); width: calc(40 * var(--u)); }
.left { transform: rotateY(-90deg) translateZ(calc(72 * var(--u))); }
.right { transform: rotateY(90deg) translateZ(calc(72 * var(--u))); }
.left::after, .right::after { content: ''; position: absolute; inset: 0; background: rgb(0 0 0 / 0.3); }
.left::after { background: rgb(0 0 0 / 0.12); }
.top, .bottom { top: calc(24 * var(--u)); height: calc(40 * var(--u)); }
.top {
background:
repeating-linear-gradient(90deg, rgb(255 255 255 / 0.18) 0 calc(1 * var(--u)), transparent calc(1 * var(--u)) calc(3 * var(--u))),
linear-gradient(var(--silver-dark), var(--silver) 20%, #f4f5fa 60%, var(--silver));
transform: rotateX(90deg) translateZ(calc(44 * var(--u)));
}
.bottom {
background: linear-gradient(var(--silver-dark), #5b6078);
transform: rotateX(-90deg) translateZ(calc(44 * var(--u)));
}
.window {
position: absolute;
top: calc(6 * var(--u));
left: calc(11 * var(--u));
box-sizing: border-box;
width: calc(26 * var(--u));
height: calc(13 * var(--u));
border: calc(1.5 * var(--u)) solid var(--silver-dark);
border-radius: calc(2 * var(--u));
background:
linear-gradient(120deg, transparent 30%, rgb(255 255 255 / 0.45) 40%, transparent 50%),
linear-gradient(160deg, #1a4a5a, #05060c);
}
.window + .window { left: auto; right: calc(11 * var(--u)); width: calc(14 * var(--u)); }
.name {
position: absolute;
top: calc(8 * var(--u));
width: 100%;
color: #4a4f66;
font: 800 calc(7 * var(--u)) Inter, system-ui, sans-serif;
letter-spacing: calc(2.5 * var(--u));
text-align: center;
text-indent: calc(2.5 * var(--u));
text-shadow: 0 calc(1 * var(--u)) 0 rgb(255 255 255 / 0.7); /* engraved */
}
/* --- the lens: a 0 × 0 anchor at its centre, on the front face --- */
.lens {
position: absolute;
left: calc(72 * var(--u));
top: calc(52 * var(--u));
transform-style: preserve-3d;
transform: translateZ(calc(20 * var(--u)));
}
.mount {
position: absolute;
left: calc(-31 * var(--u));
top: calc(-31 * var(--u));
width: calc(62 * var(--u));
height: calc(62 * var(--u));
border-radius: 50%;
background: radial-gradient(circle, #0b0c12 0 76%, var(--silver-dark) 80%, #f4f5fa 88%, var(--silver) 94%, var(--silver-dark));
transform: translateZ(calc(0.5 * var(--u)));
}
/* 16 strips, radius 25 units, 26 units long. Strip top = at the body, bottom = the front. */
.strip {
--a: calc(var(--i) * 22.5deg);
position: absolute;
left: calc(-5.3 * var(--u));
top: calc(-13 * var(--u));
width: calc(10.6 * var(--u));
height: calc(26 * var(--u));
backface-visibility: hidden;
background:
linear-gradient(rgb(0 0 0 / calc(0.36 - 0.34 * cos(var(--a) + 35deg))) 0 0), /* light from top left */
linear-gradient(transparent 0 calc(5 * var(--u)), rgb(0 0 0 / 0.5) calc(5 * var(--u)) calc(6 * var(--u)), transparent calc(6 * var(--u)) calc(17 * var(--u)), #ff4d9d calc(17 * var(--u)) calc(18 * var(--u)), transparent calc(18 * var(--u))),
repeating-linear-gradient(90deg, #2a2c3a 0 calc(1.5 * var(--u)), #0d0e14 calc(1.5 * var(--u)) calc(3 * var(--u))) 0 calc(6 * var(--u)) / 100% calc(11 * var(--u)) no-repeat, /* knurled grip */
linear-gradient(#1a1b24 0 calc(17 * var(--u)), var(--silver) calc(17 * var(--u)), #f4f5fa calc(22 * var(--u)), var(--silver-dark));
transform: rotateZ(var(--a)) translateY(calc(-25 * var(--u))) rotateX(90deg) translateY(calc(13 * var(--u)));
}
.ring,
.glass {
position: absolute;
border-radius: 50%;
}
.ring {
left: calc(-25.5 * var(--u));
top: calc(-25.5 * var(--u));
width: calc(51 * var(--u));
height: calc(51 * var(--u));
background: radial-gradient(circle, transparent 0 calc(17 * var(--u)), #05060a calc(17.5 * var(--u)) calc(18.5 * var(--u)), var(--silver-dark) calc(19 * var(--u)), #f4f5fa calc(21.5 * var(--u)), var(--silver) calc(23 * var(--u)), var(--silver-dark));
transform: translateZ(calc(26 * var(--u)));
}
.glass {
left: calc(-18 * var(--u));
top: calc(-18 * var(--u));
width: calc(36 * var(--u));
height: calc(36 * var(--u));
overflow: hidden; /* flat disc: clipping is safe */
background:
radial-gradient(circle, #05060a 0 calc(4 * var(--u)), transparent calc(5 * var(--u))),
radial-gradient(circle, transparent 0 calc(9 * var(--u)), rgb(139 108 255 / 0.5) calc(10 * var(--u)), transparent calc(12 * var(--u))),
conic-gradient(from 200deg, #183a4e, #0a0c18 25%, #463a8a 50%, #0a0c18 75%, #183a4e);
transform: translateZ(calc(22 * var(--u))); /* recessed 4 units behind the ring */
}
/* the reflection of a window: runs the rocking in reverse */
.glass em {
position: absolute;
top: calc(5 * var(--u));
left: calc(9 * var(--u));
width: calc(12 * var(--u));
height: calc(16 * var(--u));
border-radius: 50% 50% 40% 40%;
background:
linear-gradient(90deg, transparent 45%, rgb(0 0 0 / 0.5) 45% 55%, transparent 55%),
linear-gradient(transparent 45%, rgb(0 0 0 / 0.5) 45% 55%, transparent 55%),
rgb(255 255 255 / 0.55);
animation: glare 7s ease-in-out infinite alternate;
}
/* --- on top --- */
.flash {
position: absolute;
top: calc(-20 * var(--u));
left: calc(12 * var(--u));
width: calc(40 * var(--u));
height: calc(20 * var(--u));
transform-style: preserve-3d;
}
.flash i {
position: absolute;
inset: 0;
background: linear-gradient(var(--silver), var(--silver-dark));
}
.flash i:nth-child(1) {
box-sizing: border-box;
border: calc(2 * var(--u)) solid var(--silver-dark);
background:
repeating-linear-gradient(transparent 0 calc(1.5 * var(--u)), rgb(0 0 0 / 0.08) calc(1.5 * var(--u)) calc(2.5 * var(--u))), /* Fresnel lines */
radial-gradient(ellipse at 50% 40%, #fff, #e7e9f3 55%, #b9bed0);
transform: translateZ(calc(12 * var(--u)));
}
.flash i:nth-child(2) { transform: rotateY(180deg) translateZ(calc(12 * var(--u))); }
.flash i:nth-child(3),
.flash i:nth-child(4) { left: calc(8 * var(--u)); width: calc(24 * var(--u)); background: linear-gradient(var(--silver-dark), #5b6078); }
.flash i:nth-child(3) { transform: rotateY(90deg) translateZ(calc(20 * var(--u))); }
.flash i:nth-child(4) { transform: rotateY(-90deg) translateZ(calc(20 * var(--u))); }
.flash i:nth-child(5) {
top: calc(-2 * var(--u));
height: calc(24 * var(--u));
background: linear-gradient(90deg, var(--silver), #f4f5fa 50%, var(--silver));
transform: rotateX(90deg);
}
/* flat discs centred on the top edge, lifted by --h, laid down */
.knob,
.dial {
position: absolute;
border-radius: 50%;
transform: translateY(calc(var(--h) * -1 * var(--u))) rotateX(90deg);
}
.knob {
left: calc(110 * var(--u));
top: calc(-8 * var(--u));
width: calc(16 * var(--u));
height: calc(16 * var(--u));
background: radial-gradient(circle, var(--silver) 0 45%, var(--silver-dark));
}
.knob-top {
background: radial-gradient(circle at 40% 35%, #fff 0 8%, #ff4d9d 35%, #8c2a57);
}
.dial {
left: calc(74 * var(--u));
top: calc(-12 * var(--u));
width: calc(24 * var(--u));
height: calc(24 * var(--u));
background: repeating-conic-gradient(#5b6078 0 5deg, var(--silver) 5deg 10deg); /* knurled edge */
}
.dial-top {
background:
linear-gradient(transparent 46%, #ff4d9d 46% 54%, transparent 54%) 50% 0 / calc(2 * var(--u)) 50% no-repeat,
radial-gradient(circle, #f4f5fa, var(--silver) 60%, var(--silver-dark) 64%, transparent 66%),
repeating-conic-gradient(#5b6078 0 5deg, var(--silver) 5deg 10deg);
}
@keyframes rock {
from { transform: rotateX(-14deg) rotateY(-30deg); }
to { transform: rotateX(-8deg) rotateY(26deg); }
}
@keyframes glare {
from { transform: translateX(calc(10 * var(--u))) rotate(-12deg); }
to { transform: translateX(calc(-4 * var(--u))) rotate(-12deg); }
}