CLAY is one of 25 sites designed and built by Formwork to show off web design, motion and taste. Here's exactly how this squishy one came together — and how you can make the same thing.
The brief was one word: joy. CLAY is a fictional journaling app that "feels like play," so the whole site had to feel touchable — like a jar of soft toys you want to press with your thumb. That pointed straight at claymorphism: puffy, rounded, candy-coloured shapes that look moulded from clay, floating over warm cream.
requestAnimationFrame loop for bob, parallax & the poke spring.main.There's no secret asset. A "clay" surface is just a bright pastel fill wearing three shadows at once: a soft outer drop shadow for weight, an inset light highlight in the top-left, and an inset dark press in the bottom-right. Together they read as a lump of clay lit from above.
.clay{
background: var(--tone);
box-shadow:
16px 22px 40px -10px var(--sh), /* outer soft drop */
inset 7px 9px 16px rgba(255,255,255,.72), /* top-left light */
inset -9px -13px 22px rgba(38,20,64,.14); /* bottom-right press */
}
Stars and hearts can't be drawn with a box, so they're tiny inline SVGs that reuse the same
logic: a base fill, two shared radial gradients for highlight and press, and a CSS
drop-shadow() that hugs the outline. The "squish" on buttons and cards is the
same shadow set, animated — hover lifts and inflates it, :active collapses it
inward so the whole thing presses into the page.
The hero shapes go one step further: poke one and it springs. A click sets a negative "squish" value that an under-damped spring pulls back to rest each frame — compress, overshoot, settle. Feeding that scalar into a squash-and-stretch scale (wider when pressed, taller on the rebound) is what sells it as real clay:
// per frame: spring the squish back toward 0
const acc = -K * s.squish - C * s.squishV;
s.squishV += acc * dt;
s.squish += s.squishV * dt;
// squash & stretch — volume-ish preserving
const sx = 1 - s.squish * 0.6; // wider when compressed
const sy = 1 + s.squish * 0.9; // flatter when compressed
requestAnimationFrame loop that pauses off-screen.--i custom property) and bob on their own CSS loops. Poke any day and a pure-CSS squash-and-stretch keyframe fires — the same idea as the hero, kept lightweight so all thirty stay smooth..loaded class added on a double requestAnimationFrame with a 400 ms failsafe, so it can't hang.js class lands on <html>; if the script never runs, everything simply shows.prefers-reduced-motion stops the bob, freezes the shapes and makes every hover instant.Everything here is static, so hosting is three commands:
git init -b main && git add -A && git commit -m "ship clay"
gh repo create formwork-clay --public --source=. --push
gh api --method POST /repos/OWNER/formwork-clay/pages \
-f 'source[branch]=main' -f 'source[path]=/'
Use relative paths (./main.js, not /main.js) because
project Pages live under /repo-name/. Drop in an empty .nojekyll file so
every asset serves verbatim, and you're live.
That's the whole recipe: three shadows, a sine wave, two rounded fonts and static hosting. The other 24 sites each swap the central trick — shaders, kinetic type, particles — but the discipline stays the same: one idea, executed until it feels effortless.