Hugo Dark Mode vs Ad Blockers (Compatibility Fix)
When dark-mode CSS hides ad containers or ad blockers strip theme variables — diagnose and fix both.
Tested on: PaperMod v8, uBlock Origin 1.62, Brave Shields, dark + light themes
Bug: Ad slots collapsed to 0px in dark mode. Ad blockers removed ins.adsbygoogle and broke layout grid.
Symptom 1: dark mode hides ad background
PaperMod dark CSS:
.dark ins.adsbygoogle { background: transparent !important; }
Fine for AdSense. My placeholder .ad-slot used background: var(--entry) same as page — looked like missing content, editors filed bugs.
Fix — visible but neutral placeholder when ad blocked or loading:
.ad-slot {
min-height: 90px;
background: color-mix(in srgb, var(--border) 30%, transparent);
border: 1px dashed var(--border);
border-radius: 6px;
}
.dark .ad-slot:not(:has(ins[data-adsbygoogle-status="done"])) {
/* fallback if :has unsupported, use .ad-slot.is-empty */
opacity: 0.6;
}
Symptom 2: uBlock removes ad node, grid collapses
Sidebar layout:
.post-layout {
display: grid;
grid-template-columns: 1fr 300px;
}
When aside.sidebar-ad empty, main column didn't expand.
Fix:
.post-layout:has(.sidebar-ad:empty) {
grid-template-columns: 1fr;
}
@supports not selector(:has(*)) {
.post-layout.no-sidebar { grid-template-columns: 1fr; }
}
JS fallback (tiny, 400 bytes):
document.querySelectorAll('.sidebar-ad').forEach(el => {
if (!el.children.length) el.closest('.post-layout')?.classList.add('no-sidebar');
});
Only load when hugo.IsProduction.
Symptom 3: filter list blocks plausible + ads
Some lists block /ads/ path — my partial was /partials/ads/ triggering cosmetic filters on unrelated divs with class ad-slot.
Renamed:
layouts/partials/monetization/ # was ads/
class="sponsor-slot" # was ad-slot
Avoid ad- prefix in class names if blockers target cosmetic rules (controversial but fixed false positives).
Dark mode toggle + localStorage (if you keep JS)
Ad refresh on theme flip broke AdSense policy (invalid activity warning).
Solution: don't re-push ads on theme change. Reload page:
// theme toggle handler
localStorage.setItem('pref-theme', next);
location.reload(); // heavy but safe for ads
I removed JS toggle entirely — prefers-color-scheme only. Problem gone.
Detect ad block (optional, non-hostile)
<script>
setTimeout(() => {
const slot = document.querySelector('.sponsor-slot ins');
if (slot && slot.offsetHeight === 0) {
document.body.classList.add('ads-blocked');
}
}, 3000);
</script>
.ads-blocked .sponsor-slot { display: none; }
.ads-blocked .post-layout { grid-template-columns: 1fr; }
Show polite "support us" link instead of empty box.
Verify
- Chrome light + dark → ad/placeholder visible dimensions stable
- uBlock enabled → no horizontal scroll, no empty sidebar gap
- View source — sponsor-slot present, no broken HTML
- AdSense policy check — no encouraged clicks messaging
hugo server
# DevTools → Rendering → emulate dark
# DevTools → Request blocking → adsbygoogle.js
Result
Layout bug tickets: 3/month → 0. Ad block rate ~28% on tech blog — grid fix improved mobile readability for those users too.