A bakery interior drawn entirely in CSS. No images, no SVG, no canvas — every
macaron, whisker and wood grain is a <div> with a border-radius.
The scene: a glass display case packed with cat-themed treats, a chalkboard of drinks, jars on a shelf, and the shop cat asleep on the counter next to somebody's cooling coffee.
| Treat | ||
|---|---|---|
| Purrfect Macaron | raspberry shells, vanilla bean middle | $3.50 |
| Meowcha Cupcake | matcha buttercream, ears included | $4.25 |
| Fishy Taiyaki | custard filled, still warm | $3.00 |
| Toe Bean Cookie | brown butter shortbread, pink icing | $2.75 |
| Choco Cat Donut | dark glaze, far too many sprinkles | $3.75 |
| Calico Cake Slice | three layers, strawberry on top | $5.50 |
| Kitten Cake Pop | strawberry shell | $2.50 |
| Pudding Paws | custard cup under burnt caramel | $4.00 |
| Whole Kitty Cake | on the shelf, 48 hours notice please | $28.00 |
Hover, tap or tab to any treat — in the case or the whole cake up on the shelf — and it lifts with a little price tag.
Click the sleeping cat and she wakes up: eyes open, ears perk, head lifts, and the z's turn into purrs. After about five seconds she settles back down on her own, so you can wake her again.
There's a ball of yarn on the floor too. Click it to pick it up, click the cat to give it to her, and hearts float up while she purrs over it. Click the yarn again to take it back.
All of that is checkboxes, labels and two animated custom properties — there is still no JavaScript on the page.
Open index.html. That's it — there's no build step needed to look at it.
npm install # only needed for the tests
npm run build # inlines the CSS into dist/
npm test # builds, then runs the browser checks
npm run shots # renders .shots/*.png for eyeballing changes
npm run deploy # builds and deploys dist/ to Cloudflare Pages via Wranglernpm run build writes:
dist/index.html— default root entry for static hosting (Cloudflare Pages, Vercel, Netlify)dist/kitty-cafe.html— the whole thing as one self-contained pagedist/artifact.html— a body fragment for hosts that supply their own shelldist/codepen.htmlanddist/codepen.css— the two CodePen panels
The scene is a container (container-type: inline-size) with a fixed 16:10
aspect ratio, and everything inside is measured in cqw or percentages. That
means the picture scales as a single piece: it looks the same at 320px as it
does at 1440px, and nothing needs a media query.
The scene also sizes itself to the window: because height follows width at a
fixed 16:10, capping the width at (100dvh - 7rem) * 1.6 is the same as
capping the height at the viewport, so the art fills a wide screen without ever
running off the bottom of a short one.
Shared cat parts — ears, eyes, mouths, blush, whiskers — live in one place and get re-tuned per treat with custom properties:
.t-macaron {
--ear-c: var(--pink);
--ear-dip: 0.9cqw; /* how far the ear tucks into a round head */
--face-y: 36%;
--eye-s: 0.88cqw;
}Every colour comes from a token in css/tokens.css. Nothing downstream invents
a value.
index.html markup for the scene and the plain-text menu
css/tokens.css colours, fonts, shared measurements
css/page.css page shell, menu list, footer
css/room.css wall, floor, sign, lamp, shelf, chalkboard, counter
css/case.css display case, glass, shelf rows, price tags
css/parts.css reusable ears, faces, blush, whiskers, berries
css/treats.css the eight treats
css/critters.css whole cake, shop cat, tip jar, mug
css/yarn.css the ball of yarn, its states and the hearts
css/motion.css idle animations and the reduced-motion opt-out
- Each treat is a real button with an accessible name that includes its price.
- Everything works with mouse, touch, and keyboard — tags show on hover and focus, so a tap or a Tab both work.
- The art is small on a phone, so the full menu is repeated as plain text below the scene. That list is the accessible source of truth.
prefers-reduced-motion: reducestops every animation and the hover lift. The price tags still fade in.
Eighteen browser tests cover all of the above, plus horizontal overflow from
320px to 1920px, treats staying inside the case at every width, the scene never
outgrowing the window, and the page containing no <script> at all.
Apache 2.0 — see LICENSE.
Made with 💖 by Pink Pixel