The Specificity Trap
A shared utility class, working everywhere but eight specific pages - and a one-property page rule nobody remembered was still there.
A shared class is supposed to mean "this looks the same
everywhere it's used." That assumption holds right up until a
page has its own leftover CSS sitting in a
<style> block, written for something else
entirely, that happens to match the same element by accident.
When that happens, nothing throws an error. The class is still
applied, exactly as written. It just loses.
The site's brand wordmark - the logo and text
treatment at the top of every hero section - is styled by a
single shared class,
.brand-mark, that renders it at a large, consistent
size across the entire site. On two service pages and six
location pages, it wasn't doing that. The logo icon rendered at
its normal size; the text next to it rendered at roughly
body-copy size - readable, but nowhere near the scale every other
page showed.
Nothing in the markup was different. Every affected page had the
exact same
<p class="brand-mark"> structure as every
unaffected page. The class was there, spelled correctly, doing
nothing.
Each of the eight affected pages carried its own
<style> block with a rule meant for ordinary
paragraph text inside that page's content area:
.service-content p, .service-content li { font-size: 0.95rem;
... }
Reasonable on its own. The problem is that
.brand-mark is applied to a
<p> tag, and on these eight pages that
<p> happens to sit inside
.service-content. Both rules matched the same
element. The shared class lost - not because of where either
rule was written, but because of what each selector is worth:
.brand-mark - one class - specificity (0, 1, 0)
.service-content p - one class plus one tag -
specificity (0, 1, 1)
A compound selector with a tag name attached outweighs a single class, full stop, regardless of which rule appears first or which file loads last. Moving the utility class's own declaration later in the stylesheet wouldn't have changed anything - the two rules aren't tied, so source order was never the deciding factor to begin with.
The first instinct with "a class isn't working" is to assume
it's missing, misspelled, or applied to the wrong element.
Checking the rendered markup on an affected page confirmed
class="brand-mark" was present and correct - which
meant the class was being overridden, not skipped.
Once two rules are both confirmed to match the same element, guessing which one wins from memory is exactly how this kind of bug slips through review. Writing out both selectors' specificity side by side - one class versus one class plus one tag - made the outcome unambiguous instead of assumed.
!important
Forcing the utility class to win with !important
would have fixed these eight pages immediately and made the
next specificity conflict harder to reason about, since
!important rules don't lose to anything short of
another !important rule. The goal was a fix that
stayed inside normal cascade rules, not one that opted out of
them.
The actual fix was a second rule -
.service-content p.brand-mark - that matches only
this exact situation and outweighs the page rule on its own
terms: one class, one tag, and one more class, a strictly
higher specificity than either of the two rules it sits
between. It doesn't touch the original page rule, and it
doesn't need to - it just wins where it needs to win.
.brand-mark (0, 1, 0) losing to
.service-content p (0, 1, 1)
.service-content p.brand-mark added at (0, 2,
1)
One additional CSS rule, added once to the shared stylesheet rather than copy-pasted into each of the eight pages individually. The harder part wasn't writing the fix - it was noticing the pattern in the first place, since the eight affected pages had nothing in common at a glance except a coincidence of markup structure that only a specificity comparison actually explained. Auditing shared CSS for conflicts like this one is part of the Web & Digital practice, especially on sites with years of accumulated page-specific styling.
Count three numbers for each selector: ID attributes, then classes/attributes/pseudo-classes, then element tags. Compare left to right - more IDs always wins regardless of the rest, then more classes, then more tags. A single class and a class-plus-tag selector differ at the second number, so the class-plus-tag selector wins even though it "looks" simpler.
!important and move on?
It would have worked here, but it trades a small, local problem
for a permanent one: an !important rule can only be
overridden by another !important rule, which means
every future style intended for that element has to know to
fight back the same way. A targeted, more-specific selector
fixes the immediate conflict without changing the rules for
anything that comes after it.
Search the codebase for every selector that could plausibly match the same elements the utility class is meant to style - in this case, every page-scoped rule targeting a bare tag name that the class also gets applied to - and compare specificity for each one directly, rather than assuming a class that works on most pages works on all of them.
It usually takes a specificity comparison, not a guess, to find out for sure.