Engineering Journal
Pdf Processor
Pdf Processor

Diff Ui Redesign Hottake

2026-05-30

Hot Take: CSS Specificity Is Not a Bug -- It's a Design Constraint You Ignored

TLDR: The cascade is deterministic. When your panel appears on the wrong tab, you did not find a CSS bug. You found a place where two rules compete for the same property and you had not decided which one should win.


The Real Problem Is Not Specificity

Every CSS specificity post-mortem sounds the same: "I accidentally overrode display:none." The surface cause is always a specificity collision. The real cause is always the same thing underneath: a rule that sets display on an element it does not own.

A helper class -- .diff-layout, .flex-col, .grid-layout, .panel-body -- does not own the visibility of the element it is applied to. Some other system owns that. A tab switcher. A toggle button. A framework router. The helper class should not set display because it has no visibility semantics. It has layout semantics.

When the helper class sets display: flex and the visibility system sets display: none, you have two authors competing over one property with neither communicating intent to the other.


The Specificity System Is Working Correctly

Here is what actually happened:

.view-panel            { display: none  }  / specificity 0,1,0 /
.diff-layout           { display: flex  }  / specificity 0,1,0 -- LATER /
.view-panel.active     { display: flex  }  / specificity 0,2,0 /

The cascade resolved this correctly. Given the three rules above, the correct output for an inactive .view-panel.diff-layout element is display: flex. Because that is what the rules say. The cascade did not make an error. The rules were wrong.

Blaming specificity for this is like blaming the compiler for a logic error. The system executed exactly as designed.


The Correct Mental Model

Every property on every element has exactly one author. When you add a new CSS rule, ask: "Am I the author of this property on this element?"

For display on a view panel: the answer is no. The tab system is. The helper class is not.

The fix is to remove the property from the helper's rule, not to add another higher-specificity override. Adding overrides is how specificity wars start.

/ Wrong: competing for display ownership /
.diff-layout { display: flex; flex-direction: column; }

/ Right: helper class only sets what it owns / .view-panel.diff-layout { flex-direction: column; }

Note: even the two-class form is a smell because it ties the layout helper to the visibility system's class name. The cleaner design is to use display only in .view-panel and .view-panel.active, and let everything else set only non-display layout properties.


Why This Keeps Happening

Toolkits train you to put display: flex in every layout rule. Every Tailwind utility class, every Bootstrap component, every tutorial: "add d-flex" or .flex or display: flex to make a flex container.

That habit is fine when you control visibility differently (via visibility: hidden, opacity: 0, height: 0 + overflow: hidden). It breaks when your visibility system uses display: none. Once you mix display: none for hiding with display: flex for layout, any helper class that also sets display becomes a mine.

The implication is not "stop using display: none" (it is the correct tool for tab systems -- nothing should be in the accessibility tree when hidden). The implication is: pick one class to own display per element and let no other class touch it.


What the Mobile Drag Bug Has in Common With This

The mobile drag bug also came down to "who owns this decision." The drag handler assumed the layout was always horizontal. The CSS said otherwise. The fix was to read the CSS -- getComputedStyle().flexDirection -- instead of guessing.

Both bugs are the same at the root: JavaScript or CSS making an assumption about state that is owned by another system. The drag code assumed it owned axis selection. The helper class assumed it owned display. Neither was true.

Ownership is the design constraint. Specificity is just the enforcement mechanism.

Read this post in the full Engineering Journal →