DEV Community

Larry Margerum
Larry Margerum

Posted on

Build Your Bowl - A CSS Only Arroz Caldo You Actually Get to Season

Frontend Challenge CSS Art Submission 🍲🥧

Inspiration

Arroz caldo is the dish that shows up in my kitchen uninvited. Someone's sick, it's raining, a day just needs marking quietly — and there it is, ginger and rice simmering on the stove before I've really decided to make it.

The thing I always loved about it growing up was that the last step was never fixed. My mother never measured the garnish. Chili if you needed heat, calamansi if you needed something sharp to cut through it, a boiled egg if the day called for something that felt like a full meal. The base was always the same. The finishing was always yours.

When I saw this challenge allowed "a sprinkle of JavaScript," I didn't want to just paint a static bowl and call it a day — soup bowls are basically a genre unto themselves in CSS art at this point. I wanted to build the one thing that actually captures what makes this dish comfort food: that you get to decide what goes in it. So the bowl starts nearly bare, and you build it yourself, one click at a time.

Demo

GitHub logo larrymargerum01 / devto-frontend-challenge-bowl

Dev.to Frontend Challenge - CSS Challenge project inspired by Arroz caldo

Click the pot (or hit "Stir the pot") to swirl whatever's currently in the bowl. Hover a topping before clicking it — the bowl will show you a faint preview of what you're about to add. Fill all five and the rim gives you a little something back.

Journey

I went in with a rule for myself: CSS does the drawing, CSS does the motion, JavaScript is only allowed to flip switches. No canvas, no SVG, nothing procedurally drawn by a script — if it moves or changes color, a stylesheet is responsible for it.

That constraint ended up shaping almost every interesting decision in the build:

The rice. Instead of eighty individual grain elements, the whole texture is one zero-size div with an box-shadow list — every grain is just an offset, a size, and a color in a single CSS property. It's a trick I'd seen in other CSS art before but never had a real reason to reach for until I needed to fill a bowl with something that had to look scattered, not gridded.

The interactivity had to be honest. My first instinct was to reach for JavaScript to handle showing/hiding garnishes, but that felt like cheating the "CSS is the star" spirit of the challenge. So each topping's visibility, its pop-in bounce, even the broth quietly warming up as you add things — all of it lives in CSS transitions reacting to a class name. The JavaScript that's left is genuinely boring: it toggles a class and updates one sentence of status text. If you open dev tools, the JS file is short enough to read in ten seconds; the stylesheet is not.

The hover preview was the one that made me happiest. Hovering a topping chip — before you've clicked anything — ghosts a faint preview of that ingredient into the bowl. That's :has() reaching from a button in the control rail all the way into the canvas, with zero JavaScript involved in making it happen. I didn't know I'd end up using :has() for something like this when I started; it came from wanting the bowl to feel responsive to intent, not just clicks.

What I got wrong first. Everything in the bowl — every grain, every scallion ring, every chili sliver — is positioned relative to the bowl's true center, and early on I had one wrapper anchored to a corner instead of the center. Nothing looked broken, exactly, just subtly off in a way that took a while to track down, because the bug was in the coordinate system, not in any one shape. That was a good reminder that in CSS art, your math has to be as disciplined as your gradients.

What I'm proud of. That the idea and the technique are actually the same thing. "Make it your own" isn't just a caption under a picture of soup — it's something you do, and everything that happens when you do it is CSS earning its keep.

What's next. I'd like repeated clicks on a topping to intensify it — more chili, more garlic — and a shareable link that captures whatever bowl you've built, so you can send someone your specific combination instead of just a screenshot.

Built with HTML, CSS (custom properties, container queries, :has(), the box-shadow particle technique), and just enough JavaScript to flip a few switches.

Top comments (0)