CSS
I build backends. On the backend, I decide what runs first. The order is mine to write, mine to read, mine to debug. CSS is the one place in the stack where I don't get to decide. The browser decides which rule wins, an
I build backends. On the backend, I decide what runs first. The order is mine to write, mine to read, mine to debug.
CSS is the one place in the stack where I don't get to decide. The browser decides which rule wins, and it decides by a precedence table I can read but not rewrite. That is the whole thing to understand about CSS. It is not a styling language with quirks. It is a rules engine, and the surprises only look like surprises until you read the table.
Here is the table, in the order the browser applies it. When two rules disagree about the same element, specificity is the score. An ID selector beats a class selector beats an element selector, no matter where each one sits in the file:
p { color: black; }
.card p { color: blue; }
#main p { color: red; }
The paragraph is red. Not because the ID rule came last in the file — it could come first and still win. Specificity outranks position. Source order only breaks ties: when two rules have the same specificity, the one that appears later wins. That is the entire conflict-resolution system, and it is deterministic. The browser is never guessing.
Then there is the escape hatch: !important. It overrides specificity entirely, and it is the one part of the system I treat as a warning sign. It works, and it breaks the system's ability to reason about itself. Once an !important is in the stylesheet, the precedence table has a hole in it — the next person reading the file can't tell which rule wins by reading the table anymore. They have to search for overrides. It is the same failure shape I keep writing about on the backend: the override is invisible to the next reader, so the system fails silently instead of loudly.
Inheritance is the other half. Some properties flow down to children unless something closer overrides them — color, font. Others don't — margin, padding. It reads like defaults in a config file: the value applies until something nearer says otherwise. Env beats file beats default, parent beats nothing, the closest rule wins. Same shape, different syntax.
That is why CSS keeps pulling me back even though I live on the backend. Every backend system I build has its own version of this table. Middleware ordering decides which handler sees the request first. Config precedence decides which value the service actually runs with. Retry priority decides which attempt goes first when everything is failing at once. In each case, the system has a deterministic answer for which rule wins, and in each case the bug is almost never in the table. The bug is in my model of the table — I assumed order meant one thing and it meant another.
So when a style doesn't apply and I can't see why, I don't reach for a bigger hammer. I ask which rule has the higher score, whether the tie was broken by position, and whether something upstream already claimed the win with an override I forgot. The answer is always in the table. It was just in a row I wasn't looking at.
CSS is a rules engine with a precedence table. Once you read it that way, the surprises stop being surprises.
I write about backend systems and the decisions hiding inside them. Follow along. Next in this series: Integrating data governance in agile projects.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.