Apr 23, 2026 · 8 min read
css webdev frontend designsystem
Have you ever changed the order of two CSS rules and broken a component without changing the logic?
.btn:hover { background : dodgerblue; } .btn [ disabled ] { background : gray; }
Both selectors have specificity (0, 1, 1) . When a button is both hovered and disabled, the browser falls back to source order. If the :hover rule comes last, the disabled button turns blue. If the [disabled] rule comes last, it stays gray.
That sounds small, but it points to a bigger problem: component state in CSS often works by overlap.
As long as a component has only one or two states, that overlap feels manageable. Once you add :hover , :active , disabled , dark mode, breakpoints, data attributes, container queries, and overrides, it stops feeling manageable very quickly. You are no longer just writing styles. You are maintaining a resolution system in your head.
And that showed up not only as accidental conflicts, but as a growing difficulty in customizing existing components safely as real requirements piled up.
That was the problem I kept running into while building component systems. Not on toy examples, but on real buttons, inputs, panels, dropdowns, and design-system primitives. The hardest part was not writing the first version of a component. It was extending it later without reopening the entire state-resolution problem.
At some point I stopped asking, “How do I write this selector?” and started asking a better question:
... continue reading