How Safari Content Blockers Hide DOM Elements with CSS
This article explains the technical mechanism behind element hiding
in the Safari web browser. It covers how Safari Content Blockers utilize
a declarative JSON syntax to define rules, how the
css-display-none action targets specific Document Object
Model (DOM) elements, and how WebKit compiles and applies these rules to
remove unwanted page elements without compromising user privacy or
browser performance.
The Declarative JSON Architecture
Unlike traditional browser extensions that use JavaScript to actively read and modify a webpage's DOM, Safari Content Blockers operate on a declarative model. Developers provide Safari with a JSON array of rules. Safari compiles these rules into efficient bytecode before a webpage even loads. This ensures that the content blocker never sees the user's browsing activity, while WebKit handles the actual element hiding natively at the engine level.
Every content blocking rule consists of two main parts:
- Trigger: Defines the conditions (such as the URL pattern) under which the rule should be activated.
- Action: Defines what Safari should do when the trigger conditions are met.
Using the
css-display-none Action
To hide specific DOM elements on a page, Safari provides the
css-display-none action type. This action requires a
companion selector field, which accepts a comma-separated
string of standard CSS selectors.
When you specify css-display-none, you are instructing
WebKit to find any HTML elements matching your CSS selectors and prevent
them from rendering on the screen.
Here is an example of a valid Safari Content Blocker JSON rule that hides specific DOM elements:
[
{
"trigger": {
"url-filter": ".*",
"if-domain": ["example.com", "www.example.com"]
},
"action": {
"type": "css-display-none",
"selector": "#top-banner, .sidebar-ad, div[data-ad-unit]"
}
}
]Breakdown of the Rule
"url-filter": ".*": This regular expression tells Safari to evaluate this rule on any URL string."if-domain": This restricts the trigger so the CSS selectors are only applied when the user visitsexample.comorwww.example.com. Leaving this out applies the selector globally to all websites."type": "css-display-none": This tells WebKit that the action is strictly cosmetic and meant to hide elements rather than block a network request."selector": This contains the exact CSS selectors targeting the DOM elements. In the example above, it targets an element with the IDtop-banner, any element with the classsidebar-ad, and anydivcontaining thedata-ad-unitattribute.
How WebKit Applies the Styles
When a user navigates to a webpage that matches the
trigger criteria, WebKit intercepts the page rendering
process. Before the DOM is painted to the screen, WebKit takes the
string provided in the selector field and injects a
high-priority user stylesheet directly into the page context.
Behind the scenes, the JSON action is translated into the following CSS rule:
#top-banner, .sidebar-ad, div[data-ad-unit] {
display: none !important;
}Because WebKit applies this at the browser engine level using the
!important flag, it overrides the webpage's native
stylesheets and inline styles. The targeted DOM elements remain in the
HTML source code, but they collapse to zero width and height, rendering
them completely invisible to the user and preventing them from taking up
layout space.
Supported CSS Selectors
The selector field supports most standard CSS3 selectors
that WebKit natively understands. You can use:
- ID selectors:
#newsletter-popup - Class selectors:
.sponsored-post - Attribute selectors:
a[href^="https://tracker.example.com"] - Combinators:
div.main-content > aside.promo - Pseudo-classes:
div:not(.essential-content)
By combining specific triggers with precise CSS selectors, Safari Content Blockers efficiently strip away unwanted DOM elements natively, avoiding the memory overhead and privacy risks of injected JavaScript.