Why CSS Still Trips Up Good Engineers
CSS has a peculiar reputation among developers. JavaScript, Python, or C++ are treated as serious disciplines worth studying deeply, while CSS is often dismissed as a toy language that just “works” until it doesn’t. Yet the truth is more nuanced: CSS is simple in structure but rarely easy in practice, as Jeremy Keith argued years ago. The confusion between those two words explains a lot of the frustration.
Because CSS syntax is quick to grasp — there are no loops, variables, or control flows to learn — newcomers assume it should behave predictably. When it doesn’t, the natural conclusion is that the language itself is broken. That reasoning ignores the fact that CSS is not a programming language but a styling descriptor for an environment that changes constantly.
The Hidden Complexity of “Simple”
The gap between simplicity and ease is exactly where developers lose patience. A snippet like display: flex or position: absolute is easy to write, but the effect is never isolated. CSS properties interact with each other in ways that are not always obvious, and they also interact with default browser styles, ancestor rules, and the responsive viewport. As Brandon Smith put it, setting one property means you’re never just setting that one thing — you are triggering a chain of reactions with a dozen other declared or implicit values.
That is why even a “trivial” concern like centering content has spawned entire guides and endless Stack Overflow answers. The question “How do I center this?” has many valid responses depending on context: flexbox, grid, margins, transforms, or text alignment — each working under different conditions.
Facing the Web’s Reality
Part of the friction comes from CSS forcing engineers to confront the inherent messiness of the web. Unlike an isolated Node.js script or a local GUI app, a web page must work across a staggering range of devices, input methods, and browsers. You need fallbacks, progressive enhancements, and defensive layouts. JavaScript lets you abstract those variables away most of the time; CSS does not. You cannot ignore the fact that a layout must remain usable on a phone, a desktop with a mouse, and a screen reader user navigating by keyboard.
This reality conflicts with the engineer’s instinct to write clean, deterministic code. But the browser is not deterministic in that sense — it renders based on viewport size, user preferences, and the cascade. Frustration often arises from trying to override the browser rather than leveraging its defaults.
Working with the Grain Instead of Against It
The solution is not to overhaul CSS or turn it into a more programmatic language. The better approach is to practice restraint and clarity. A rule of thumb worth remembering: never be more explicit than necessary. Web pages are responsive by default — writing CSS that respects that natural behavior avoids the fight against the rendering engine.
- Use
min-widthinstead ofwidthwhere possible so content can shrink without breaking. - Prefer percentage or viewport units over rigid media queries when they achieve the intent.
- Think in terms of rules and meaning rather than adding properties until something looks right.
The web calls this “webishness”: the condition of existing in an environment you do not fully control. Once you accept that CSS is about describing intent within that environment — not dictating exact outcomes — the frustration gives way to a more predictable workflow. Respect the language’s strengths, and it stops feeling like a broken tool.



