Blog / Web Dev

Beyond the Checkbox Hack: When CSS State Machines Actually Matter

Most components don't need seven states. But when they do, pure CSS beats JavaScript complexity every time.

Juan David Avellaneda April 15, 2026 3 min read 6 views
Beyond the Checkbox Hack: When CSS State Machines Actually Matter

The Problem Nobody Talks About

Last month I rebuilt a client's image carousel for the third time. First attempt: JavaScript. Second: a React component that felt like overkill. Third time, I stopped and asked myself—what if this just lived in CSS?

The checkbox hack works great for toggles. You click, something happens, you click again, it undoes. Binary. Clean. But real interfaces don't live in binaries. A modal can be closed, opening, open, closing, or error. A filter panel has idle, active, loading, results, and empty states. That's five states, and suddenly you're drowning in class names and state management.

This is where the radio state machine enters. I'm not sure this is the right move, but—it might save you from adding Redux for something that should just be HTML and CSS.

How It Actually Works

  • Hidden radio inputs grouped by name
  • Each input represents a single, mutually exclusive state. You click one, the others deactivate automatically because that's how radio buttons work in the DOM
  • CSS selectors like input[value="open"]:checked ~ .modal handle the visual output
  • No JavaScript needed. Actually, no state management library needed
  • Mobile friendly by default

The elegance here is almost stupid. Radio buttons have solved this problem since 1995. We forgot about them.

Where I'm Actually Using This

At a product studio in Medellín—not mine, but I consult—they built a settings panel with tabs. Old way: React state, onChange handlers, maybe Zustand. New way: four radio inputs named "tab", each with a different value. CSS does the rest. The bundle size difference was 12KB. Small? Maybe. But the code reads like English instead of state machine pseudocode.

I'm skeptical whether this scales to something massive, honestly. I haven't tested it against a component with fifteen states. That might break. But for the 70% of components that need three to six modes? This is cleaner than the alternatives.

There's also a UX thing people miss: radio buttons have native accessibility. Screen readers understand them. They have built-in focus management. You get that for free instead of building custom ARIA attributes and managing focus yourself.

The Tradeoff Nobody Mentions

CSS state machines make certain things impossible. You can't animate between states smoothly the way you can with JavaScript. You can't trigger external side effects—API calls, analytics, validation. If your state machine needs to do anything beyond toggle CSS classes, this approach falls apart immediately and you're back to JavaScript.

Also, debugging. When something goes wrong, you're digging through CSS selectors and the shadow DOM of form inputs. It's not fun. JavaScript state is verbose but at least you can console.log it.

I'm not sure this is the right move, but I keep wondering if we're treating CSS state machines as more powerful than they are. They're useful. Not revolutionary.

The Real Question

Here's what I actually think about: does your component exist to manage state, or does state exist to serve the component? If you're building something where the state is the product—a dashboard, a form wizard, something complex—reach for JavaScript. Radio state machines are for UI flourishes. Modals. Tabs. Accordions. Components where the state is almost invisible to the user.

One more thing. I checked the CSS Tricks article that started this whole conversation, and it's solid work. Solid enough that I'm probably missing something. There might be advanced use cases with nested state machines or something I haven't tried yet. But I'm shipping radio state machines in three projects right now, and they're working.

The question isn't whether CSS state machines are better than JavaScript frameworks. It's whether you've actually considered them as an option before you

#CSS #state-management #frontend #performance #UX

Was this helpful?

Juan David Avellaneda

Juan David Avellaneda

Innovation Specialist · Bogotá, Colombia