Možná si myslíš, že prefix ad-
v CSS je neškodný. Vždyť je to jen pár písmen, jasně čitelné i pro juniory. Jenže právě těch pár písmen dokáže tvému frontendu udělat dokonale prázdnou stránku.
Ještě horší? O chybě se dozvíš až od zuřivého klienta. V produkci. V pátek odpoledne. S GA4 tichým jako pěna, protože se nenačetl ani pixel.
Takže: chceš-li přežít a zachovat si UX (a duševní zdraví), čti dál. Dnes ti ukážu, proč ad-blokátory loví všechno, co zavání reklamou, kde přesně se pálíme nejčastěji a jak se tomu vyhnout bez šarlatánství typu „anti-ad-block skript“.
Audio verze článku
Délka záznamu 8 minut
Proč na tom záleží?
EasyList – základní muničák většiny ad-blokátorů – obsahuje tisíce pravidel, která se spouštějí ještě dřív, než blikne první pixel tvého layoutu. A mezi nimi i zdánlivě nevinné řádky jako:
##[id^="ad-"]
##[class*=" ads-"]
Pozn. ## je zápis z adblockeru, normální css selector je bez něj.
Tahle regexová kudla skryje každý element, jehož id
začíná na ad-
nebo class
obsahuje samostatné „ads“ .
Podobně síťové filtry stopnou requesty na URL typu /ads/
, /banner/
, nebo subdoménu ads.example.com
(easylist.to). Výsledek? Soubor se nenačte, JavaScript hodí chybu, a ty dumáš, proč je komponenta rozbitá jen „u některých uživatelů“.
Varování: nejsou to jen ID a class
Blokátory dnes umí „kosmetické“ filtry i nad textovým obsahem – třeba :-abp-contains()
schová <span>Reklama</span>
čistě podle vnitřního textu (help.adblockplus.org). Jednoduché? Ani náhodou.
Jak loví blokátory tvé selektory?
- CSS injection – v manifestu extension se přidá stylopis s globálními selektory (viz výše).
- URL rewrite & cancel – pokud request odpovídá filtru, padá rovnou na síťové vrstvě.
- Scriptlet hijacking – uBlock Origin dovede vložit JS, jenž selektivně ruší či mění DOM.
Bacha, tady se chybuje nejčastěji: Když prvek nejdřív není v DOMu (lazy render) a ad-blokátor mezitím stihl nasypat filtr, skryje se okamžitě po injectu. Přepočet layoutu ti pak celé UI posune a pixel-perfektní grid je v háji.
Příklady z praxe
- U projektu, který prodává SaaS pro SMB, bylo tlačítko „Add-ons“ (
id="addons_btn"
). Nevinné, že? Dostali skvělý nápad mu dát novou tříduclass="ads-variant"
a konverze spadla o 32 %. Proč? Uživatelé s uBlockem tlačítko prostě neviděli. Víte jak se to blbě debaguje 🙂 ? - Několikrát jsem jsem viděl, třeba subdoménu pro Server GTM pojmenovanou GTM, analytics, 🙂 etc. a pak takové server GTM je blokované asi i více než to základní 🙂 . Proto doporučuji do takových subdomén dávat jména jako „chleba“, „zarovka“, „auto“ a nebo cokoliv náhodného, ale rozhodně nic spojeného s analytikou nebo reklamou.
- Znám projekt kde dali do názvu hlavní domény slovíčko analytics a pokud jste kliknuli na odkaz směřující na tuto doménu, tak vám to házelo varování 🙂 . To nebylo dobry, myslím si že ten projekt časem umřel.
- Tip od Robin Pokorný: I generovaný hash class HTML prvku může obsahovat slov „ad“ třeba hodnota „ad01“. Aneb další malá podmínku do kódu 🙂 .
- Tip od Martin Maly: Bacha i na názvy cookies, web vytvářel cookies obsahující ve jméně „ident“ a ty byli u uživatelů v prohlížeči mazány. Za mě i bacha na názvy jako „ga“, „analytics“ „id“ u názvů cookies, občas jsou bloknuté taky.
- A jelikož existují i blokátory cookie lišt, tak je velmi dobré se v nich vyhnout názvům a ID jako je cookie, consent etc. Aneb uživatel, co ani neuvidí vaši cookie lištu, tak vám jistě souhlas nedá.
Kde děláme chyby nejčastěji?
- Adresářová struktura:
/static/**ads**/banner.png
- Hasherem generované ID:
gtm-ad-1234
- WordPress šablony:
theme-adwise
,widget_banner
- Alt a title:
alt="Advertisement"
- Data atributy:
data-ad-slot="header"
- Subdoména:
ads.mydomain.cz
A vždy stejný scénář: „U mě na lokále to funguje, v produkci u 30 % návštěv ne.“
Jak z toho ven?
1. Pojmenuj to jinak
Používej projektově-specifické aliasy: místo ad-slot
třeba slotHero
, slotSidemenu
. Uvnitř slova nevadí (he*ad*er
, ro*ad*map
) – filtry většinou vyžadují hranice slova.
2. Hashuj cesty k assetům
Pokud CDN vyžaduje strukturu /img/ads/…
, zaveď reverse-proxy přepis na /img/a1b2c3/…
. Je to jeden Nginx rewrite a máš klid.
Z praxe webového analytika: Dříve se dokonce detekovali adblockery přes soubor pojmenovaný „ads.js“ a pokud se nenašetl a nezapsala se proměnné, tak jste věděli, že uživatel blokuje reklamu.
3. Odděl semantiku od layoutu
Potřebuješ-li prvek označit kvůli legislativnímu „Sponsored” štítku, dej mu dvě třídy:
<span class="slotA1 sponsorLabel">Sponsored</span>
Při stylování počítej, že sponsorLabel
může zmizet – velikost a padding nech raději na rodiči.
4. Detect & fallback
const slot = document.querySelector('#slotHero');
if (!slot || slot.offsetHeight === 0) {
console.warn('Adblock? Switching to fallback.');
renderFallback(slot);
}
Žádná detekce API? Využij starý dobrý DOM.
Začni hned – budoucí já ti poděkuje.
Checklist před releasem
- Audit názvů – projdi build artefakty (
grep -R --line-number -E '\\b(ad|banner|promo|sponsor|analytics)\\b' dist/
). - Lokální test – nainstaluj uBlock Origin a AdGuard. Obojí, občas mají různé feedy.
- Spusť Lighthouse + ad-block – sleduj rozdíl v JS errors.
- Check GA4 hits – trafí měření i s blokátorem? Pokud ne, zvaž server-side GTM proxy.
- E2E test na CI – Cypress + uBlock ve headless Chrome.
- Monitoruješ rozbitý layout? – jednoduchý visual-diff screenshot test.
- Log fallbacků – když se spustí
renderFallback
, pošli event do Sentry. - Nastav alerty – 5% nárůst fallbacků == Slack ping.
- Re-audit při každém major refactoru frontendu.
- Dokumentuj – ať příští kolega ví, proč existuje
slotHero
a neadHeader
.
Osobní tipy, které mi zachránily karieru
„Pojmenuj to nejdřív ošklivě, pak zkrášli“
Když vymýšlím ID komponent, napíšu schválně hnusný placeholder: XXX_NEJSEM_AD
. Refaktoruju teprve, jakmile vidím layout na dev serveru. Pomáhá to zabránit reflexu napsat první, co mě napadne („adBox“).
„Mockni si ad-blokátor v API“
Potřebuješ-li ladit bez extension, nech reverse-proxy mazat /ads/
requesty. Rychlejší iterace, stejný výsledek.
„Neustále grepuj diff“
Hook v pre-commit, který zakáže commity obsahující \bads?\b
v nových souborech, ti ušetří studené poty.
Shrnutí
- Ad-blokátory nečtou úmysly, jen regexy.
- Klíčová slova
ad
,ads
,banner
,promo
a slova příbuzná stejného významu v ID, class, URL či souboru ⇒ prvek zmizí. - K pádu layoutu stačí jeden skrytý wrapper.
- Řešení je primitivní: přejmenovat a fallbackovat.
- Automatizuj detekci, jinak na to zapomeneš.
Co máte udělat po přečtení článku?
- Udělat si kontrolu u sebe na webu.
- Přidat si to do dokumentace pro vývojáře,
Jak to vidíte vy? Máte vlastní horror-story s id="adBanner"
nebo jste ustáli nasazení bez ztrát? Podělte se v komentářích na sociálních sítích, ať se příště smějeme společně, ne jednotně plačeme.
Zkrácena verze, co jsem dal k nám do dokumentace:
Vyhýbejte se slovům „ad“, „ads“, „advert“, „banner“… v identifikátorech a cestách
Platí pro: id
, class
, atributy data-*
, názvy souborů a adresářů, subdomény, URL parametrů
Pravidlo
- Nikdy nepoužívejte uvedená klíčová slova ani jejich zkrácené/rozšířené varianty (ad, ads, adv, advert, banner, promo, sponsor…)
- …pokud stojí na začátku nebo za oddělovačem (
-
,_
,/
,.
).
- …pokud stojí na začátku nebo za oddělovačem (
- Povoleno je, když řetězec leží uvnitř jiného slova (header, roadmap).
Proč
- Blokátory reklamy (uBlock Origin, AdBlock Plus, AdGuard…) se řídí seznamy jako EasyList.
- Obsahují globální CSS filtry skrývající prvky podle selektorů typu
##[id^="ad-"]
,##.ads-banner
stackoverflow.com - A síťové filtry, které ruší požadavky na URL s částmi
/ads/
,/banner/
či doménamiads.example.com
adblockplus.org
- Obsahují globální CSS filtry skrývající prvky podle selektorů typu
- Jakmile blokátor pravidlo trefí, prvek dostane
display:none
nebo se request vůbec nespustí → layout se rozpadne či kód selže „beze stopy“. - Incidenty s ID
ad_holder
, tématem „adwise“ apod. ukazují, že jde o častý zdroj skrytých chyb.
Důsledky porušení
- Neviditelné komponenty (tlačítka, modaly, ikony…)
- Chybějící JS/CSS soubory ⇒ JS errors, rozhozený design
- Obtížné ladění – poskytovatelé i QA často nemají ad-block zapnutý
Výjimky
Kde je to bezpečné | Proč |
---|---|
Viditelný text („Reklama“, „Sponsored“) | Filtry umísťují slovo do layoutu jen kosmeticky; nespoléhejte na jeho rozměry |
Komentáře v HTML/JS, interní proměnné | Blokátory je neparsují |
Příklady
❌ Nesprávně | ✅ Správně |
---|---|
<div id="ad_holder"> | <div id="slotHero"> |
/static/ads/logo.svg | /static/assets/logo.svg |
class="banner_top" | class="heroTop" |
Dodržováním tohoto pravidla předejdete nečekanému skrývání prvků a ušetříte hodiny pátrání po „záhadně mizících“ komponentech.
Protože to zaujalo i lidi z mezinárodních teamů, tak tu mám i verzi v angličtině.