Cosmetic Filtering, and Why V1 Skips It
Cosmetic filtering hides page elements with CSS, such as the empty frame left when an advert is blocked. It needs a content script that reads the page. Scalpel version 1 blocks at the network layer only, so it does no cosmetic filtering yet.
Why it matters
When a blocker stops an advert from loading, the page still knows that advert was supposed to be there. It leaves behind an empty box: a gap in the layout where the image or video would have gone. Most users notice. Network blocking removes the advert's bytes and speeds up the page, but it can't tidy up the space.
Cosmetic filtering solves that problem. It uses CSS rules to hide the empty boxes, smooth out the layout, and make blocked adverts truly invisible to the eye. Some blockers do this well. Scalpel version 1 chooses not to, because of how the extension architecture works.
How it works
Cosmetic filtering rules hide page elements using CSS selectors. The classic rule syntax, borrowed from Adblock Plus, uses two hash symbols and a selector. For example:
##.advertisement-box
##div[data-ad-slot]
When the blocker loads a page, a content script (a piece of code that runs inside the page) applies these rules. It finds every matching element and hides it with CSS (display: none).
The element is still there in the page's HTML (the blocker doesn't delete anything from the source), but it's invisible. So the page layout flows around the gap, other content shifts up to fill the space, and the blocked advert disappears completely.
This works well for layout problems. If an ad network always puts adverts in <div class="sponsor">, one cosmetic rule hides all of them at once.
What does not matter
Cosmetic rules cannot see inside an advert. They can't tell a harmless sponsored link from a deceptive casino ad. They work by pattern matching HTML elements, not by reading the advert's content. So cosmetic filtering is not a substitute for network blocking. It's complementary: network blocking removes the advert's bytes (faster pages, less tracking), and cosmetic filtering removes the empty box.
Also, cosmetic filtering is not "selective." All cosmetic rules run on every page, even if the advert they target isn't there. The selector just matches nothing and does nothing. There's no performance cost, but there's no clever targeting either.
Code example
A rule file that uses cosmetic filtering might look like this:
! Hide all elements with class ad-banner
##.ad-banner
##.ads-container
! Hide sponsored content by data attribute
##[data-ad-unit="sidebar"]
! Complex selector: hide only images inside divs with class sponsor
##div.sponsor > img
When Scalpel v1 runs, it ignores these rules. A cosmetic rule would need a content script (code running inside your pages), and that script would need permission to read the page structure. Scalpel's declarativeNetRequest approach doesn't use content scripts at all. Chrome applies the blocking rules itself, and the extension never sees your pages.
How Scalpel Ads Blocker shows it
Scalpel v1 does no cosmetic filtering. When an advert is blocked at the network layer, the empty box stays on the page. Most of the time, the page's CSS already handles it gracefully. The layout reflows, text shifts up, and the gap closes on its own.
If a page breaks badly because of a blocked advert's empty space, the best fix is to allowlist the site so that advert loads. Or you can use another blocker that does cosmetic filtering alongside Scalpel.
This is a conscious trade-off. By avoiding content scripts, Scalpel doesn't need host-page access and stays more private. As Manifest V3 matures and cosmetic filtering tools improve, a future version might add an opt-in cosmetic layer. For now, network blocking is the focus.