Opus 5.5
Hostedvia Claude CodeOpenRouter
Show the code the model wroteHide the code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Four Skies</title>
<meta name="description" content="Four weather cards (sun, rain, snow, thunderstorm) animated in pure CSS.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..700&family=Martian+Mono:wght@400;500&display=swap">
<style>
/* ==========================================================================
Four Skies: pure-CSS weather cards
No JavaScript, no SVG, no images. Every drop, flake and bolt is an
element or a pseudo-element. Scene geometry is written in cqw (card
width) so each card scales as one drawing; a card is 100cqw x 140cqw.
========================================================================== */
:root {
--page: #e6eaf0;
--page-ink: #131a24;
--page-muted: #536073;
--page-rule: #c7cfd9;
--radius: 22px;
--display: "Bricolage Grotesque", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--mono: "Martian Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
color-scheme: light;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--page: #0b0f16;
--page-ink: #e5eaf1;
--page-muted: #8793a4;
--page-rule: #222a36;
color-scheme: dark;
}
}
:root[data-theme="dark"] {
--page: #0b0f16;
--page-ink: #e5eaf1;
--page-muted: #8793a4;
--page-rule: #222a36;
color-scheme: dark;
}
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
background: var(--page);
color: var(--page-ink);
font-family: var(--display);
-webkit-font-smoothing: antialiased;
}
.wrap {
max-width: 1240px;
margin-inline: auto;
padding-inline: clamp(16px, 4vw, 48px);
padding-block: clamp(32px, 7vw, 88px) clamp(32px, 6vw, 64px);
}
/* ---------- page intro ---------- */
.intro { max-width: 62ch; }
.eyebrow {
margin: 0;
font: 500 11px/1.4 var(--mono);
letter-spacing: .16em;
text-transform: uppercase;
color: var(--page-muted);
}
.intro h1 {
margin: 14px 0 12px;
font-size: clamp(38px, 6vw, 64px);
font-weight: 600;
font-variation-settings: "opsz" 96;
line-height: .95;
letter-spacing: -.035em;
text-wrap: balance;
}
.lede {
margin: 0;
font-size: clamp(15px, 1.6vw, 17px);
line-height: 1.55;
color: var(--page-muted);
text-wrap: pretty;
}
.note {
margin: clamp(20px, 3vw, 32px) 0 0;
padding-top: 14px;
border-top: 1px solid var(--page-rule);
max-width: 62ch;
font: 400 11px/1.6 var(--mono);
letter-spacing: .03em;
color: var(--page-muted);
}
/* ---------- the deck: a row of four, 2 x 2 when narrow ---------- */
.deck {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: clamp(12px, 2vw, 22px);
max-width: 780px;
margin-top: clamp(28px, 4.5vw, 52px);
}
@media (min-width: 1040px) {
.deck { grid-template-columns: repeat(4, minmax(0, 1fr)); max-width: none; }
}
@media (max-width: 320px) {
.deck { grid-template-columns: 1fr; }
}
/* ==========================================================================
Card chrome: shared by all four
========================================================================== */
.card {
position: relative;
aspect-ratio: 5 / 7;
container-type: inline-size;
overflow: hidden;
isolation: isolate;
border-radius: var(--radius);
color: #fff;
background: var(--sky);
box-shadow:
0 1px 2px rgb(10 20 35 / .10),
0 26px 48px -26px var(--cast);
animation: rise .9s cubic-bezier(.2, .75, .25, 1) calc(var(--i) * 110ms) backwards;
}
/* glass rim + top sheen, above everything */
.card::after {
content: "";
position: absolute;
inset: 0;
z-index: 10;
border-radius: inherit;
pointer-events: none;
background: linear-gradient(170deg, rgb(255 255 255 / .11), rgb(255 255 255 / 0) 30%);
box-shadow:
inset 0 0 0 1px rgb(255 255 255 / .10),
inset 0 1px 0 rgb(255 255 255 / .24);
}
@keyframes rise {
from { opacity: 0; transform: translateY(16px) scale(.985); }
}
.scene { position: absolute; inset: 0; }
.card__head {
position: absolute;
z-index: 8;
top: 7cqw;
left: 7cqw;
right: 7cqw;
text-shadow: 0 1px 2px rgb(0 12 32 / .2);
}
.card__label {
margin: 0;
font: 500 max(10px, 3.5cqw)/1 var(--mono);
letter-spacing: .24em;
text-transform: uppercase;
opacity: .9;
}
.card__temp {
margin: 2.4cqw 0 0 -1.1cqw;
font-family: var(--display);
font-size: 23cqw;
font-weight: 300;
font-variation-settings: "opsz" 96;
line-height: .86;
letter-spacing: -.05em;
}
.card__temp span { font-weight: 200; }
.card__foot {
position: absolute;
z-index: 8;
left: 7cqw;
right: 7cqw;
bottom: 0;
height: 28cqw;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1.4cqw;
color: var(--ground-ink);
}
.card__place {
font: 500 max(12px, 5.2cqw)/1.05 var(--display);
letter-spacing: -.01em;
}
.card__stat {
font: 400 max(9.5px, 3.2cqw)/1.2 var(--mono);
letter-spacing: .04em;
opacity: .78;
}
/* ground band: sky above, ground below, weather in between.
Its top edge is the horizon / puddle line at y = 112cqw. */
.ground {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 28cqw;
background: var(--ground);
box-shadow: inset 0 1px 0 var(--horizon);
}
/* ==========================================================================
Cloud component: base pill + two puffs (+ optional third).
Positioned by the base's top-left corner; --u scales the whole cloud.
One vertical gradient, re-registered on every part, so the shading
runs continuously across the union of shapes.
========================================================================== */
.cloud {
--u: 1cqw;
position: absolute;
width: calc(var(--u) * 62);
height: calc(var(--u) * 18);
border-radius: calc(var(--u) * 9);
background-image: var(--fill);
background-size: 100% calc(var(--u) * 32);
background-position: 0 calc(var(--u) * -14);
background-repeat: no-repeat;
filter: drop-shadow(0 calc(var(--u) * 1.4) calc(var(--u) * 1.8) var(--shadow, rgb(20 40 70 / .2)));
}
.cloud::before,
.cloud::after,
.cloud .puff {
content: "";
position: absolute;
border-radius: 50%;
background-image: var(--fill);
background-size: 100% calc(var(--u) * 32);
background-repeat: no-repeat;
box-shadow: inset 0 calc(var(--u) * 1.1) calc(var(--u) * 1.3) var(--rim, rgb(255 255 255 / .38));
}
.cloud::before {
width: calc(var(--u) * 28);
height: calc(var(--u) * 28);
left: calc(var(--u) * 24);
top: calc(var(--u) * -14);
background-position: 0 0;
}
.cloud .puff {
width: calc(var(--u) * 17);
height: calc(var(--u) * 17);
left: calc(var(--u) * 41);
top: calc(var(--u) * -6);
background-position: 0 calc(var(--u) * -8);
}
.cloud::after {
width: calc(var(--u) * 20);
height: calc(var(--u) * 20);
left: calc(var(--u) * 8);
top: calc(var(--u) * -7);
background-position: 0 calc(var(--u) * -7);
}
.cloud--main { animation: bob var(--bob) ease-in-out var(--phase) infinite alternate; }
.cloud--back { animation: loiter var(--loiter) ease-in-out var(--phase) infinite alternate; }
@keyframes bob {
from { transform: translateY(-.7cqw); }
to { transform: translateY(.7cqw); }
}
@keyframes loiter {
from { transform: translateX(-4cqw); }
to { transform: translateX(4cqw); }
}
/* ==========================================================================
SUN
========================================================================== */
.card--sun {
--sky:
radial-gradient(70% 48% at 58% 48%, rgb(255 236 180 / .5), rgb(255 236 180 / 0) 70%),
linear-gradient(180deg, #2670cb 0%, #3a89df 34%, #76b4ec 62%, #bcdcf3 80%);
--ground: linear-gradient(180deg, #f7e2b3 0%, #efc682 100%);
--horizon: rgb(255 255 255 / .7);
--ground-ink: #56380c;
--cast: rgb(26 76 150 / .6);
/* the passing cloud, its shadow and the sun's dimming share one clock */
--pass: 30s;
--pass-o: -3s;
}
.sun-core { position: absolute; left: 58cqw; top: 67cqw; }
.sun-dim { position: absolute; animation: sun-dim var(--pass) ease-in-out var(--pass-o) infinite; }
.glow {
position: absolute;
width: 94cqw;
height: 94cqw;
left: -47cqw;
top: -47cqw;
border-radius: 50%;
background: radial-gradient(circle closest-side,
rgb(255 218 120 / .8) 0%,
rgb(255 200 90 / .42) 30%,
rgb(255 184 72 / .14) 58%,
rgb(255 176 64 / 0) 100%);
animation: glow 6.4s ease-in-out var(--phase) infinite;
}
.rays {
position: absolute;
width: 76cqw;
height: 76cqw;
left: -38cqw;
top: -38cqw;
border-radius: 50%;
background: repeating-conic-gradient(from 0deg,
rgb(255 242 200 / 0) 0deg,
rgb(255 242 200 / .6) 2.6deg,
rgb(255 242 200 / 0) 5.2deg 30deg);
-webkit-mask-image: radial-gradient(circle closest-side, transparent 42%, #000 48%, rgb(0 0 0 / .45) 72%, transparent 100%);
mask-image: radial-gradient(circle closest-side, transparent 42%, #000 48%, rgb(0 0 0 / .45) 72%, transparent 100%);
animation: spin 64s linear infinite;
}
.rays--fine {
width: 60cqw;
height: 60cqw;
left: -30cqw;
top: -30cqw;
background: repeating-conic-gradient(from 15deg,
rgb(255 250 225 / 0) 0deg,
rgb(255 250 225 / .7) 1.2deg,
rgb(255 250 225 / 0) 2.4deg 30deg);
-webkit-mask-image: radial-gradient(circle closest-side, transparent 52%, #000 58%, transparent 100%);
mask-image: radial-gradient(circle closest-side, transparent 52%, #000 58%, transparent 100%);
animation: spin 96s linear infinite reverse;
}
.disc {
position: absolute;
width: 30cqw;
height: 30cqw;
left: -15cqw;
top: -15cqw;
border-radius: 50%;
background: radial-gradient(circle at 40% 36%, #fffdf3 0%, #fff1b8 22%, #ffd860 56%, #ffbe34 88%, #ffb02a 100%);
box-shadow:
0 0 0 1.4cqw rgb(255 226 132 / .26),
0 0 6cqw 1cqw rgb(255 198 72 / .55),
inset -1.6cqw -2cqw 4cqw rgb(255 146 28 / .32);
}
/* cloud shadow sweeping the whole card as the drifter crosses the sun */
.shade {
position: absolute;
inset: 0;
background: rgb(18 40 74);
opacity: 0;
animation: shade var(--pass) ease-in-out var(--pass-o) infinite;
}
.cloud--drifter {
--u: .65cqw;
--fill: linear-gradient(180deg, #ffffff 0%, #f4f8fc 55%, #d4e0ec 100%);
--shadow: rgb(24 64 124 / .25);
--rim: rgb(255 255 255 / .9);
left: 0;
top: 74cqw;
animation: pass var(--pass) linear var(--pass-o) infinite;
}
@keyframes spin { to { rotate: 360deg; } }
@keyframes glow {
0%, 100% { transform: scale(.9); opacity: .78; }
50% { transform: scale(1.06); opacity: 1; }
}
/* 0 -> 60%: drift across (fully off-card at both ends), 60 -> 100%: clear sky */
@keyframes pass {
0% { transform: translateX(-46cqw); }
60%, 100% { transform: translateX(104cqw); }
}
/* cloud centre crosses the sun centre at ~33% of the clock */
@keyframes sun-dim {
0%, 19%, 47%, 100% { opacity: 1; }
28%, 38% { opacity: .5; }
33% { opacity: .4; }
}
@keyframes shade {
0%, 19%, 47%, 100% { opacity: 0; }
29%, 37% { opacity: .11; }
}
/* ==========================================================================
Rain lanes (RAIN + STORM)
Each <i> is a lane from inside the cloud down to the puddle line.
i::before the falling streak (head lands at 80% of the cycle)
i::after the ripple ring (fires at 80%)
i > b the splash crown (fires at 80%)
All three share --d / --o, so they stay in sync. Each lane also wanders
sideways on its own slower clock, so splashes never repeat on a spot.
========================================================================== */
.drops {
position: absolute;
top: 64cqw;
bottom: 28cqw; /* lane floor = puddle line */
--fall: 48cqw;
transform-origin: 50% 100%;
}
.drops i {
position: absolute;
top: 0;
bottom: 0;
left: var(--x);
width: 0;
animation: lane var(--ld) ease-in-out var(--lo) infinite alternate;
}
.drops i::before {
content: "";
position: absolute;
bottom: 100%;
left: calc(var(--w) / -2);
width: var(--w);
height: calc(var(--len) * var(--s));
border-radius: 0 0 var(--w) var(--w);
background: linear-gradient(to bottom, rgb(var(--drop) / 0), rgb(var(--drop) / .95));
opacity: calc(var(--s) * .82);
transform-origin: 50% 100%;
animation: fall var(--d) linear var(--o) infinite;
}
.drops i::after {
content: "";
position: absolute;
top: 100%;
left: calc(var(--rip) / -2);
width: var(--rip);
height: calc(var(--rip) * .28);
margin-top: calc(var(--rip) * -.14);
border-radius: 50%;
border: max(1px, .3cqw) solid rgb(var(--drop) / .85);
opacity: 0;
animation: ripple var(--d) cubic-bezier(.2, .6, .35, 1) var(--o) infinite;
}
.drops b {
position: absolute;
top: 100%;
left: -.3cqw;
width: .6cqw;
height: .6cqw;
margin-top: -.3cqw;
border-radius: 50%;
scale: var(--s);
box-shadow:
-1.3cqw -.2cqw 0 0 rgb(var(--drop) / .9),
1.2cqw -.5cqw 0 0 rgb(var(--drop) / .9),
.1cqw -1.3cqw 0 -.08cqw rgb(var(--drop) / .9);
opacity: 0;
animation: crown var(--d) ease-out var(--o) infinite;
}
@keyframes lane {
from { translate: calc(var(--dx) * -1) 0; }
to { translate: var(--dx) 0; }
}
/* 0-80%: fall at constant speed. 80-90%: the tail drains into the
puddle at the same speed. 90-100%: parked (invisible) at the line.
The jump back to 0% happens inside the cloud. */
@keyframes fall {
0% { transform: translateY(0) scaleY(1); }
80% { transform: translateY(var(--fall)) scaleY(1); }
90%, 100% { transform: translateY(var(--fall)) scaleY(0); }
}
@keyframes ripple {
0%, 79% { opacity: 0; transform: scale(.12); }
81% { opacity: .95; transform: scale(.34); }
100% { opacity: 0; transform: scale(calc(.55 + var(--s) * .45)); }
}
@keyframes crown {
0%, 80% { opacity: 0; transform: translateY(0) scale(.3); }
82% { opacity: .9; }
88% { transform: translateY(-1.3cqw) scale(.85); }
97% { opacity: 0; transform: translateY(-.1cqw) scale(1.15); }
100% { opacity: 0; transform: translateY(0) scale(.3); }
}
/* ==========================================================================
RAIN
========================================================================== */
.card--rain {
--sky: linear-gradient(180deg, #4f6478 0%, #687d91 38%, #8799ab 66%, #a2b1bf 80%);
--ground:
radial-gradient(44cqw 9cqw at 53cqw 0, rgb(206 218 230 / .28), rgb(206 218 230 / 0)),
linear-gradient(180deg, #5b6c7c 0%, #435261 100%);
--horizon: rgb(222 234 246 / .5);
--ground-ink: #ffffff;
--cast: rgb(40 56 76 / .6);
--bob: 6.8s;
--loiter: 17s;
}
.card--rain .cloud--back {
--u: .72cqw;
--fill: linear-gradient(180deg, #b9c4cf 0%, #a0adba 60%, #8e9ba8 100%);
--rim: rgb(255 255 255 / .18);
--shadow: rgb(20 36 54 / .12);
left: 52cqw;
top: 45cqw;
opacity: .78;
}
.card--rain .cloud--main {
--fill: linear-gradient(180deg, #b2bdc8 0%, #8c9aa8 55%, #6d7b89 100%);
--rim: rgb(255 255 255 / .3);
--shadow: rgb(20 34 52 / .28);
left: 22cqw;
top: 52cqw;
}
.drops--rain {
left: 22cqw;
width: 48cqw;
transform: skewX(-8deg);
--drop: 228 239 250;
--len: 8cqw;
--w: max(1.2px, .55cqw);
--rip: 5.4cqw;
}
/* ==========================================================================
SNOW
========================================================================== */
.card--snow {
--sky: linear-gradient(180deg, #5a7ba2 0%, #7c9bbe 38%, #aac1d8 68%, #d3e0ec 80%);
--ground: linear-gradient(180deg, #f4f8fc 0%, #e2ebf4 55%, #d0deea 100%);
--horizon: rgb(255 255 255 / .9);
--ground-ink: #243c5a;
--cast: rgb(50 80 120 / .5);
--bob: 8.4s;
}
.card--snow .cloud--main {
--fill: linear-gradient(180deg, #ffffff 0%, #eef3f9 55%, #cfdcea 100%);
--rim: rgb(255 255 255 / .95);
--shadow: rgb(30 60 100 / .22);
left: 22cqw;
top: 52cqw;
}
/* Flakes: <i> sways on its own clock; i::before falls (translate) and,
for crystals, spins (rotate). Fall and sway periods never divide
each other, so no flake repeats its path. */
.flakes {
position: absolute;
top: 60cqw;
left: 23cqw;
width: 56cqw;
height: 53cqw;
--fall: 53cqw; /* ends 1cqw into the snowbank */
}
.flakes i {
position: absolute;
top: 0;
left: var(--x);
animation: sway var(--sd) ease-in-out var(--so) infinite alternate;
}
.flakes i::before {
content: "";
position: absolute;
width: var(--s);
height: var(--s);
left: calc(var(--s) / -2);
top: calc(var(--s) / -2);
border-radius: 50%;
background: radial-gradient(circle closest-side, #fff 0 50%, rgb(255 255 255 / 0) 100%);
opacity: 0;
animation: snowfall var(--d) linear var(--o) infinite;
}
.flakes i.xl::before {
--t: .17cqw;
border-radius: 0;
background:
radial-gradient(circle closest-side, #fff 0 16%, rgb(255 255 255 / 0) 30%),
linear-gradient(0deg, transparent calc(50% - var(--t) - .4px), #fff calc(50% - var(--t)) calc(50% + var(--t)), transparent calc(50% + var(--t) + .4px)),
linear-gradient(60deg, transparent calc(50% - var(--t) - .4px), #fff calc(50% - var(--t)) calc(50% + var(--t)), transparent calc(50% + var(--t) + .4px)),
linear-gradient(120deg, transparent calc(50% - var(--t) - .4px), #fff calc(50% - var(--t)) calc(50% + var(--t)), transparent calc(50% + var(--t) + .4px));
-webkit-mask-image: radial-gradient(circle closest-side, #000 78%, transparent 100%);
mask-image: radial-gradient(circle closest-side, #000 78%, transparent 100%);
filter: drop-shadow(0 0 .4cqw rgb(255 255 255 / .75));
animation:
snowfall var(--d) linear var(--o) infinite,
spin var(--rd) linear 0s infinite var(--dir);
}
@keyframes sway {
from { transform: translateX(calc(var(--sa) * -1)); }
to { transform: translateX(var(--sa)); }
}
@keyframes snowfall {
0% { translate: 0 0; opacity: 0; }
12% { opacity: var(--a); }
82% { opacity: var(--a); }
100% { translate: 0 var(--fall); opacity: 0; }
}
/* drifted snow sitting on the ground line */
.bank {
position: absolute;
left: 0;
right: 0;
bottom: 27cqw;
height: 6cqw;
background:
radial-gradient(21cqw 4.6cqw at 11cqw 100%, #f4f8fc 96%, rgb(244 248 252 / 0) 100%),
radial-gradient(27cqw 3.4cqw at 43cqw 100%, #f4f8fc 96%, rgb(244 248 252 / 0) 100%),
radial-gradient(19cqw 5.4cqw at 73cqw 100%, #f4f8fc 96%, rgb(244 248 252 / 0) 100%),
radial-gradient(16cqw 3cqw at 97cqw 100%, #f4f8fc 96%, rgb(244 248 252 / 0) 100%);
filter: drop-shadow(0 -.3cqw .5cqw rgb(255 255 255 / .6));
}
.glint {
position: absolute;
left: var(--x);
top: var(--y);
width: 1.4cqw;
height: 1.4cqw;
margin: -.7cqw 0 0 -.7cqw;
border-radius: 50%;
background: radial-gradient(circle closest-side, #fff, rgb(255 255 255 / 0));
opacity: 0;
animation: glint var(--gd) ease-in-out var(--go) infinite;
}
.glint::before,
.glint::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
width: 3.4cqw;
height: .22cqw;
margin: -.11cqw 0 0 -1.7cqw;
background: linear-gradient(90deg, rgb(255 255 255 / 0), #fff, rgb(255 255 255 / 0));
}
.glint::after { transform: rotate(90deg); }
@keyframes glint {
0%, 74%, 100% { opacity: 0; transform: scale(.3) rotate(0deg); }
84% { opacity: 1; transform: scale(1) rotate(45deg); }
}
/* ==========================================================================
STORM
========================================================================== */
.card--storm {
--sky: linear-gradient(180deg, #111624 0%, #1b2234 38%, #2a3246 68%, #363f56 80%);
--ground:
radial-gradient(50cqw 8cqw at 45cqw 0, rgb(150 168 210 / .14), rgb(150 168 210 / 0)),
linear-gradient(180deg, #262d3d 0%, #181c27 100%);
--horizon: rgb(170 190 232 / .3);
--ground-ink: #e8edf6;
--cast: rgb(8 12 24 / .75);
--bob: 5.6s;
--loiter: 14s;
}
.card--storm .cloud--back {
--u: 1.05cqw;
--fill: linear-gradient(180deg, #30384b 0%, #262d3e 60%, #1e2331 100%);
--rim: rgb(255 255 255 / .06);
--shadow: rgb(0 0 0 / .3);
left: 46cqw;
top: 30cqw;
}
.card--storm .cloud--main {
--u: 1.14cqw;
--fill: linear-gradient(180deg, #5b647a 0%, #3b4254 55%, #252a37 100%);
--rim: rgb(255 255 255 / .13);
--shadow: rgb(0 0 0 / .4);
left: 17cqw;
top: 50cqw;
}
.drops--storm {
left: 12cqw;
width: 52cqw;
--drop: 196 210 240;
--len: 9.5cqw;
--w: max(1.1px, .5cqw);
--rip: 4.6cqw;
animation: gust 4.6s ease-in-out -1.2s infinite alternate;
}
@keyframes gust {
from { transform: skewX(-11deg); }
to { transform: skewX(-16deg); }
}
/* Lightning.
Three unrelated clocks (8.3s, 13.1s, 5.9s) so the pattern of strikes
never lines up the same way twice:
A: one hard full-card flash + bolt
C: a strike and a re-strike down the same channel
sheet: cloud-internal flicker, no bolt
Every flash rises in 1-2 frames and decays; no single clock produces
more than two flashes per second. */
.flash {
position: absolute;
inset: 0;
opacity: 0;
}
.flash--a {
background: radial-gradient(130% 95% at 36% 50%, rgb(252 253 255 / .98), rgb(226 233 255 / .9) 52%, rgb(190 204 248 / .8));
animation: flash-a 8.3s linear -.8s infinite;
}
.flash--c {
background: radial-gradient(130% 95% at 70% 50%, rgb(250 252 255 / .96), rgb(222 230 255 / .86) 52%, rgb(186 200 246 / .74));
animation: flash-c 13.1s linear -3.4s infinite;
}
.bolt {
position: absolute;
opacity: 0;
background: radial-gradient(38% 4% at 30% 100%, rgb(226 234 255 / .9), rgb(226 234 255 / 0));
filter:
drop-shadow(0 0 .6cqw rgb(255 255 255))
drop-shadow(0 0 3.2cqw rgb(140 174 255));
-webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 16%);
mask-image: linear-gradient(to bottom, transparent 0%, #000 16%);
}
.bolt::before,
.bolt::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, #ffffff, #e8f0ff);
}
.bolt::before {
clip-path: polygon(51.5% 0, 43.6% 12%, 56.8% 22%, 39.2% 40%, 51.6% 50%, 34% 66%, 45.2% 74%, 30% 100%,
48.8% 74%, 38% 66%, 56.4% 50%, 44.8% 40%, 63.2% 22%, 50.4% 12%, 58.5% 0);
}
.bolt::after {
clip-path: polygon(50.7% 50%, 63.9% 61%, 60.6% 69%, 75% 83%, 62.4% 69%, 66.1% 61%, 53.3% 50%);
}
.bolt--a {
left: 28cqw;
top: 60cqw;
width: 22cqw;
height: 52cqw;
animation: bolt-a 8.3s linear -.8s infinite;
}
.bolt--c {
left: 57cqw;
top: 63cqw;
width: 18cqw;
height: 49cqw;
transform: scaleX(-1);
animation: bolt-c 13.1s linear -3.4s infinite;
}
.sheet {
position: absolute;
left: 8cqw;
top: 24cqw;
width: 88cqw;
height: 56cqw;
border-radius: 50%;
background: radial-gradient(closest-side, rgb(214 224 255 / .8), rgb(170 190 255 / .3) 55%, rgb(170 190 255 / 0));
mix-blend-mode: screen;
opacity: 0;
animation: sheet 5.9s linear -1.2s infinite;
}
/* 8.3s clock: 1% = 83ms */
@keyframes flash-a {
0%, 30% { opacity: 0; }
30.2% { opacity: .94; }
30.8% { opacity: .78; }
31.3% { opacity: .42; }
31.8% { opacity: .36; }
33.6% { opacity: 0; }
100% { opacity: 0; }
}
@keyframes bolt-a {
0%, 30% { opacity: 0; }
30.2% { opacity: 1; }
31.6% { opacity: .9; }
32.9% { opacity: 0; }
100% { opacity: 0; }
}
/* 13.1s clock: 1% = 131ms */
@keyframes flash-c {
0%, 58% { opacity: 0; }
58.15% { opacity: .8; }
58.55% { opacity: .48; }
58.9% { opacity: .05; }
59.05% { opacity: .9; }
59.5% { opacity: .6; }
61% { opacity: 0; }
100% { opacity: 0; }
}
@keyframes bolt-c {
0%, 58% { opacity: 0; }
58.15% { opacity: 1; }
58.7% { opacity: .75; }
58.9% { opacity: .08; }
59.05% { opacity: 1; }
60% { opacity: .85; }
61% { opacity: 0; }
100% { opacity: 0; }
}
/* 5.9s clock: 1% = 59ms */
@keyframes sheet {
0%, 70% { opacity: 0; }
71% { opacity: .75; }
72% { opacity: .3; }
72.8% { opacity: .65; }
76% { opacity: 0; }
100% { opacity: 0; }
}
/* ==========================================================================
Reduced motion: freeze every scene mid-cycle (negative delays make that a
natural still), drop the flashes, keep one bolt so STORM still reads.
========================================================================== */
@media (prefers-reduced-motion: reduce) {
.card { animation: none; }
.scene *,
.scene *::before,
.scene *::after { animation-play-state: paused !important; }
.flash,
.sheet { display: none; }
.bolt--a { animation: none; opacity: .85; }
}
</style>
</head>
<body>
<main class="wrap">
<header class="intro">
<p class="eyebrow">No JavaScript · No SVG · No images</p>
<h1>Four Skies</h1>
<p class="lede">Every raindrop, snowflake and lightning bolt below is an HTML element or pseudo-element, moved by CSS keyframes alone.</p>
</header>
<section class="deck" aria-label="Current conditions">
<article class="card card--sun" style="--i:0; --phase:0s">
<div class="scene" aria-hidden="true">
<div class="ground"></div>
<div class="sun-core">
<div class="sun-dim">
<span class="glow"></span>
<span class="rays"></span>
<span class="rays rays--fine"></span>
</div>
<span class="disc"></span>
</div>
<div class="shade"></div>
<div class="cloud cloud--drifter"></div>
</div>
<div class="card__head">
<h2 class="card__label">Sun</h2>
<p class="card__temp">31<span>°</span></p>
</div>
<div class="card__foot">
<span class="card__place">Seville</span>
<span class="card__stat">UV index 9</span>
</div>
</article>
<article class="card card--rain" style="--i:1; --phase:-1.3s">
<div class="scene" aria-hidden="true">
<div class="ground"></div>
<div class="cloud cloud--back"></div>
<div class="drops drops--rain">
<i style="--x:81.4%;--d:.95s;--o:-.92s;--s:1.01;--ld:7.9s;--lo:-1.6s;--dx:2.2cqw"><b></b></i>
<i style="--x:53.4%;--d:.92s;--o:-.5s;--s:.83;--ld:9.3s;--lo:-2.2s;--dx:2.8cqw"><b></b></i>
<i style="--x:60.6%;--d:1s;--o:-.05s;--s:.81;--ld:5.7s;--lo:-3s;--dx:1.5cqw"><b></b></i>
<i style="--x:34.7%;--d:.89s;--o:-.82s;--s:.92;--ld:8s;--lo:-4.2s;--dx:1.5cqw"><b></b></i>
<i style="--x:98.3%;--d:.96s;--o:-.61s;--s:.73;--ld:9.3s;--lo:-.3s;--dx:2.4cqw"><b></b></i>
<i style="--x:43.9%;--d:.83s;--o:-.47s;--s:1.04;--ld:8.2s;--lo:-8s;--dx:2.6cqw"><b></b></i>
<i style="--x:94.8%;--d:.86s;--o:-.44s;--s:1.14;--ld:4.7s;--lo:-4.3s;--dx:2cqw"><b></b></i>
<i style="--x:48.9%;--d:.94s;--o:-.6s;--s:.84;--ld:5.8s;--lo:-.5s;--dx:2.1cqw"><b></b></i>
<i style="--x:22.2%;--d:.95s;--o:-.14s;--s:.88;--ld:5.3s;--lo:-4.2s;--dx:2.3cqw"><b></b></i>
<i style="--x:5.4%;--d:.96s;--o:-.82s;--s:.86;--ld:7.2s;--lo:-3.8s;--dx:1.9cqw"><b></b></i>
<i style="--x:29.6%;--d:.87s;--o:-.3s;--s:.94;--ld:6.3s;--lo:-1s;--dx:1.9cqw"><b></b></i>
<i style="--x:76.8%;--d:.98s;--o:-.58s;--s:.73;--ld:5.7s;--lo:-4.1s;--dx:1.7cqw"><b></b></i>
<i style="--x:74.4%;--d:.92s;--o:-.65s;--s:.9;--ld:6.7s;--lo:-.7s;--dx:2.6cqw"><b></b></i>
<i style="--x:88.2%;--d:.9s;--o:-.44s;--s:.98;--ld:6.2s;--lo:-.9s;--dx:1.8cqw"><b></b></i>
<i style="--x:27.8%;--d:.93s;--o:-.18s;--s:.92;--ld:6.8s;--lo:-.7s;--dx:2cqw"><b></b></i>
<i style="--x:20%;--d:.96s;--o:-.54s;--s:.76;--ld:7.9s;--lo:-7.4s;--dx:2cqw"><b></b></i>
<i style="--x:56.8%;--d:.94s;--o:-.55s;--s:.78;--ld:7.6s;--lo:-2.5s;--dx:1.8cqw"><b></b></i>
<i style="--x:37.9%;--d:.98s;--o:-.01s;--s:.87;--ld:7.7s;--lo:-3.1s;--dx:2.1cqw"><b></b></i>
<i style="--x:11.2%;--d:1.02s;--o:-.02s;--s:.78;--ld:8.2s;--lo:-4.2s;--dx:1.5cqw"><b></b></i>
<i style="--x:69.3%;--d:.87s;--o:-.36s;--s:1.04;--ld:5.5s;--lo:-4.9s;--dx:1.6cqw"><b></b></i>
<i style="--x:.7%;--d:.84s;--o:-.45s;--s:1.06;--ld:7.9s;--lo:-7.2s;--dx:1.8cqw"><b></b></i>
<i style="--x:86.6%;--d:.91s;--o:-.68s;--s:1.03;--ld:5.7s;--lo:-2.4s;--dx:2.4cqw"><b></b></i>
<i style="--x:14.3%;--d:.89s;--o:-.42s;--s:.98;--ld:7.7s;--lo:-1.5s;--dx:1.9cqw"><b></b></i>
<i style="--x:63.6%;--d:.89s;--o:-.13s;--s:1.16;--ld:9.3s;--lo:-5.7s;--dx:1.6cqw"><b></b></i>
</div>
<div class="cloud cloud--main"></div>
</div>
<div class="card__head">
<h2 class="card__label">Rain</h2>
<p class="card__temp">12<span>°</span></p>
</div>
<div class="card__foot">
<span class="card__place">Bergen</span>
<span class="card__stat">Rain 6 mm/h</span>
</div>
</article>
<article class="card card--snow" style="--i:2; --phase:-2.9s">
<div class="scene" aria-hidden="true">
<div class="ground"></div>
<div class="flakes">
<i style="--x:27.7%;--s:2.32cqw;--a:.86;--d:6.08s;--o:-5.67s;--sd:3.24s;--so:-1.33s;--sa:1.7cqw"></i>
<i style="--x:78.6%;--s:1.29cqw;--a:.56;--d:7.78s;--o:-7.01s;--sd:2.98s;--so:-2.61s;--sa:2.4cqw"></i>
<i style="--x:18.5%;--s:2.39cqw;--a:.88;--d:6.57s;--o:-6.04s;--sd:2.87s;--so:-.81s;--sa:2cqw"></i>
<i class="xl" style="--x:40.4%;--s:4.86cqw;--a:1;--d:6.98s;--o:-4.52s;--sd:2.61s;--so:-1.93s;--sa:3.9cqw;--rd:10.8s;--dir:normal"></i>
<i style="--x:52.7%;--s:1.39cqw;--a:.58;--d:7.55s;--o:-.18s;--sd:3.48s;--so:-3.12s;--sa:2.5cqw"></i>
<i style="--x:19.9%;--s:2cqw;--a:.77;--d:7.07s;--o:-3.99s;--sd:2.34s;--so:-.89s;--sa:2.7cqw"></i>
<i style="--x:63.5%;--s:2.66cqw;--a:.96;--d:5.5s;--o:-4.92s;--sd:1.95s;--so:-.54s;--sa:2.5cqw"></i>
<i class="xl" style="--x:10.9%;--s:4.62cqw;--a:1;--d:5.61s;--o:-4.47s;--sd:3.49s;--so:-2.35s;--sa:2.5cqw;--rd:10.5s;--dir:reverse"></i>
<i style="--x:47.2%;--s:1.6cqw;--a:.65;--d:7.27s;--o:-.57s;--sd:3.24s;--so:-.66s;--sa:1.4cqw"></i>
<i style="--x:37%;--s:1.23cqw;--a:.54;--d:8.09s;--o:-4.03s;--sd:2.58s;--so:-1.88s;--sa:3.1cqw"></i>
<i style="--x:71.9%;--s:2.49cqw;--a:.91;--d:6.82s;--o:-.86s;--sd:2.84s;--so:-2.48s;--sa:2.7cqw"></i>
<i class="xl" style="--x:55%;--s:4.76cqw;--a:1;--d:5.93s;--o:-.32s;--sd:2.68s;--so:-.56s;--sa:3.8cqw;--rd:9.9s;--dir:reverse"></i>
<i style="--x:10.9%;--s:2.57cqw;--a:.93;--d:5.71s;--o:-5.03s;--sd:3.35s;--so:-2.03s;--sa:2.5cqw"></i>
<i style="--x:41.1%;--s:2.12cqw;--a:.8;--d:7.23s;--o:-3.12s;--sd:2.49s;--so:-2.08s;--sa:1.6cqw"></i>
<i style="--x:99.5%;--s:2.52cqw;--a:.92;--d:5.97s;--o:-1.7s;--sd:3.42s;--so:-1.29s;--sa:2.9cqw"></i>
<i class="xl" style="--x:92.5%;--s:5.15cqw;--a:1;--d:6.14s;--o:-2.01s;--sd:3.19s;--so:-1.77s;--sa:2.7cqw;--rd:13.7s;--dir:reverse"></i>
<i style="--x:5.9%;--s:2.73cqw;--a:.98;--d:5.98s;--o:-2.78s;--sd:3.51s;--so:-1.84s;--sa:2.8cqw"></i>
<i style="--x:57%;--s:1.62cqw;--a:.65;--d:8.32s;--o:-7.52s;--sd:3.22s;--so:-.53s;--sa:3cqw"></i>
<i style="--x:45%;--s:2.6cqw;--a:.94;--d:5.67s;--o:-2.66s;--sd:2.97s;--so:-2.1s;--sa:1.1cqw"></i>
<i class="xl" style="--x:72.3%;--s:4.48cqw;--a:1;--d:6.36s;--o:-3.25s;--sd:3.76s;--so:-.8s;--sa:2.4cqw;--rd:12.3s;--dir:normal"></i>
<i style="--x:23.9%;--s:2.43cqw;--a:.89;--d:6.7s;--o:-1.97s;--sd:2.09s;--so:-1.5s;--sa:3.1cqw"></i>
<i style="--x:89.4%;--s:1.75cqw;--a:.69;--d:7.45s;--o:-5.36s;--sd:3.41s;--so:-2.05s;--sa:1.7cqw"></i>
<i style="--x:82.8%;--s:1.14cqw;--a:.51;--d:8.4s;--o:-5.36s;--sd:3.48s;--so:-.84s;--sa:2.9cqw"></i>
<i class="xl" style="--x:27.8%;--s:5.38cqw;--a:1;--d:6.87s;--o:-2.87s;--sd:2.95s;--so:-2.37s;--sa:2.3cqw;--rd:7.4s;--dir:reverse"></i>
<i style="--x:87.8%;--s:2.36cqw;--a:.87;--d:6.28s;--o:-1.31s;--sd:3.54s;--so:-2.27s;--sa:1.6cqw"></i>
<i style="--x:73.9%;--s:1.68cqw;--a:.67;--d:7.3s;--o:-6.01s;--sd:2.51s;--so:-1.29s;--sa:1.1cqw"></i>
<i style="--x:95.4%;--s:2.37cqw;--a:.87;--d:6.11s;--o:-2.52s;--sd:2.62s;--so:-.91s;--sa:1.8cqw"></i>
<i style="--x:33.1%;--s:2.64cqw;--a:.95;--d:5.71s;--o:-4.88s;--sd:3.37s;--so:-2.72s;--sa:2.4cqw"></i>
<i style="--x:1.8%;--s:1.71cqw;--a:.68;--d:7.37s;--o:-3.54s;--sd:2.09s;--so:-.15s;--sa:2.1cqw"></i>
<i style="--x:58.6%;--s:2.11cqw;--a:.8;--d:6.37s;--o:-4.45s;--sd:3.15s;--so:-1.17s;--sa:1.1cqw"></i>
<i style="--x:67.8%;--s:1.88cqw;--a:.73;--d:7.62s;--o:-3.62s;--sd:2.39s;--so:-.59s;--sa:1.9cqw"></i>
<i style="--x:13.6%;--s:1.82cqw;--a:.71;--d:7.81s;--o:-1.86s;--sd:2.37s;--so:-.65s;--sa:1.8cqw"></i>
</div>
<div class="bank"></div>
<i class="glint" style="--x:13cqw; --y:108.6cqw; --gd:5.3s; --go:-1.1s"></i>
<i class="glint" style="--x:47cqw; --y:110.2cqw; --gd:7.1s; --go:-4.6s"></i>
<i class="glint" style="--x:76cqw; --y:108.2cqw; --gd:6.2s; --go:-2.4s"></i>
<div class="cloud cloud--main"></div>
</div>
<div class="card__head">
<h2 class="card__label">Snow</h2>
<p class="card__temp">−4<span>°</span></p>
</div>
<div class="card__foot">
<span class="card__place">Tromsø</span>
<span class="card__stat">Snow 2 cm/h</span>
</div>
</article>
<article class="card card--storm" style="--i:3; --phase:-4.1s">
<div class="scene" aria-hidden="true">
<div class="ground"></div>
<div class="cloud cloud--back"></div>
<div class="drops drops--storm">
<i style="--x:12%;--d:.58s;--o:-.46s;--s:1.06;--ld:5.6s;--lo:-2.2s;--dx:1.2cqw"><b></b></i>
<i style="--x:83.2%;--d:.58s;--o:-.36s;--s:1.23;--ld:4.3s;--lo:-2.7s;--dx:1.4cqw"><b></b></i>
<i style="--x:37.9%;--d:.51s;--o:-.31s;--s:1.27;--ld:6.4s;--lo:-.5s;--dx:1.8cqw"><b></b></i>
<i style="--x:61.5%;--d:.66s;--o:-.3s;--s:.95;--ld:5.6s;--lo:-3s;--dx:1.3cqw"><b></b></i>
<i style="--x:67.9%;--d:.65s;--o:-.51s;--s:.79;--ld:7.1s;--lo:-6.6s;--dx:1.5cqw"><b></b></i>
<i style="--x:72.7%;--d:.56s;--o:-.31s;--s:1.1;--ld:4.9s;--lo:-.2s;--dx:2.5cqw"><b></b></i>
<i style="--x:91.1%;--d:.65s;--o:-.25s;--s:.78;--ld:4.9s;--lo:-2.1s;--dx:2.4cqw"><b></b></i>
<i style="--x:71%;--d:.56s;--o:-.35s;--s:1.26;--ld:5.1s;--lo:-1.3s;--dx:1.2cqw"><b></b></i>
<i style="--x:64.3%;--d:.57s;--o:-.22s;--s:1.29;--ld:3.7s;--lo:-1.3s;--dx:2.5cqw"><b></b></i>
<i style="--x:18.2%;--d:.68s;--o:-.33s;--s:.8;--ld:4.5s;--lo:-1.2s;--dx:1.3cqw"><b></b></i>
<i style="--x:41.4%;--d:.66s;--o:-.6s;--s:.85;--ld:5.6s;--lo:-4.3s;--dx:1.9cqw"><b></b></i>
<i style="--x:55.3%;--d:.54s;--o:-.12s;--s:1.29;--ld:6s;--lo:-4.6s;--dx:2.2cqw"><b></b></i>
<i style="--x:51.4%;--d:.57s;--o:-.56s;--s:1.06;--ld:5.2s;--lo:-3.7s;--dx:2.2cqw"><b></b></i>
<i style="--x:19.2%;--d:.52s;--o:-.4s;--s:1.29;--ld:5.2s;--lo:-1s;--dx:2.4cqw"><b></b></i>
<i style="--x:36.9%;--d:.62s;--o:-.46s;--s:.83;--ld:7.3s;--lo:-2.3s;--dx:1.9cqw"><b></b></i>
<i style="--x:14.8%;--d:.56s;--o:-.02s;--s:1.16;--ld:4.3s;--lo:-3.5s;--dx:1.6cqw"><b></b></i>
<i style="--x:97.9%;--d:.53s;--o:-.03s;--s:1.24;--ld:4.4s;--lo:-1.2s;--dx:1.6cqw"><b></b></i>
<i style="--x:23.5%;--d:.58s;--o:-.36s;--s:1.21;--ld:7.5s;--lo:-7.4s;--dx:2.5cqw"><b></b></i>
<i style="--x:76.6%;--d:.64s;--o:-.14s;--s:.86;--ld:7s;--lo:-3s;--dx:1.5cqw"><b></b></i>
<i style="--x:80.9%;--d:.67s;--o:-.35s;--s:.81;--ld:5.3s;--lo:-4.3s;--dx:1.7cqw"><b></b></i>
<i style="--x:9%;--d:.55s;--o:-.15s;--s:1.13;--ld:3.9s;--lo:-1.5s;--dx:1.2cqw"><b></b></i>
<i style="--x:47.2%;--d:.58s;--o:-.46s;--s:1.1;--ld:3.8s;--lo:-3.5s;--dx:1.3cqw"><b></b></i>
<i style="--x:27.2%;--d:.56s;--o:-.41s;--s:1.18;--ld:3.6s;--lo:-1.5s;--dx:1.5cqw"><b></b></i>
<i style="--x:33.8%;--d:.61s;--o:-.12s;--s:.87;--ld:5.4s;--lo:-2.7s;--dx:1.4cqw"><b></b></i>
<i style="--x:94.1%;--d:.58s;--o:-.38s;--s:1.12;--ld:7.2s;--lo:-2.4s;--dx:2.5cqw"><b></b></i>
<i style="--x:1.4%;--d:.62s;--o:-.34s;--s:.86;--ld:4.1s;--lo:-.4s;--dx:1.3cqw"><b></b></i>
<i style="--x:29.4%;--d:.62s;--o:-.2s;--s:.97;--ld:4.8s;--lo:-4.8s;--dx:1.4cqw"><b></b></i>
<i style="--x:45.9%;--d:.56s;--o:-.35s;--s:1.12;--ld:4.7s;--lo:-.7s;--dx:1.6cqw"><b></b></i>
<i style="--x:57.4%;--d:.64s;--o:-.21s;--s:.93;--ld:3.5s;--lo:-2.6s;--dx:1.6cqw"><b></b></i>
<i style="--x:87%;--d:.66s;--o:-.27s;--s:.92;--ld:4.7s;--lo:-.4s;--dx:1.3cqw"><b></b></i>
<i style="--x:89.2%;--d:.58s;--o:-.22s;--s:1.22;--ld:7s;--lo:-1.5s;--dx:2.1cqw"><b></b></i>
<i style="--x:4.2%;--d:.59s;--o:-.46s;--s:.98;--ld:7.5s;--lo:-3s;--dx:2.4cqw"><b></b></i>
</div>
<div class="cloud cloud--main"><i class="puff"></i></div>
<div class="flash flash--a"></div>
<div class="flash flash--c"></div>
<div class="bolt bolt--a"></div>
<div class="bolt bolt--c"></div>
<div class="sheet"></div>
</div>
<div class="card__head">
<h2 class="card__label">Storm</h2>
<p class="card__temp">24<span>°</span></p>
</div>
<div class="card__foot">
<span class="card__place">Darwin</span>
<span class="card__stat">Gusts 70 km/h</span>
</div>
</article>
</section>
<p class="note">When your system asks for reduced motion, the scenes freeze mid-frame and the lightning stops flashing.</p>
</main>
</body>
</html>One email when a new model lands on the board or the showcase. Unsubscribe anytime.