A tiny, local browser extension that hides webpage content containing configurable words.
No server. No tracking. No analytics.
When a page contains a configured word (default: Trump), the extension
hides the smallest useful piece of content around it — a paragraph,
heading, list item, or table cell, and only a whole section or
article when no smaller block exists — and keeps doing so for content
added later by dynamic pages. Non-matching content is untouched.
| without filter | with filter |
|---|---|
![]() |
![]() |
- Local-only processing: everything runs in your browser.
- Configurable word list with a small options page.
- Local hide counter on the options page — the total never leaves your device.
- Case-insensitive whole-word matching (
TrumpmatchesTrump's, nottrumpet). - Dynamic content filtering via
MutationObserver. - Reversible: hidden content is marked with a CSS class, never deleted.
- Chrome and Firefox from one codebase (Manifest V3).
- Zero runtime dependencies, automated tests, reproducible builds.
- Download the latest
browser-word-filter-chrome.zipfrom Releases. - Extract it.
- Open
chrome://extensions. - Enable Developer mode.
- Click Load unpacked.
- Select the extracted
extensiondirectory.
- Download the latest
browser-word-filter-firefox.zipfrom Releases. - Open
about:debugging#/runtime/this-firefox. - Click Load Temporary Add-on.
- Select
manifest.jsoninside the extractedextensiondirectory.
Open any webpage containing a configured word. Matching content will be hidden automatically. Change the word list via the extension's options page.
| Load unpacked | Options page |
|---|---|
![]() |
![]() |
git clone https://github.com/kibotu/browser-filter.git
cd browser-filter
npm ci
npm test
npm run buildGenerated packages appear in:
dist/
├── chrome/
│ └── browser-word-filter-chrome.zip
└── firefox/
└── browser-word-filter-firefox.zip
To try the extension locally, load extension/ unpacked (Chrome) or
temporary (Firefox) — no build required.
| Command | Purpose |
|---|---|
npm run lint |
ESLint over all JavaScript |
npm test |
Vitest unit, DOM, mutation, and config tests |
npm run coverage |
Tests with coverage thresholds enforced |
npm run validate |
JSON, manifest schema, source hygiene, ZIPs |
npm run build |
Reproducible Chrome + Firefox archives |
npm run icons |
Regenerate extension icons (no dependencies) |
Vitest with jsdom. Coverage thresholds are enforced and CI fails below them:
Statements: 90% Branches: 85% Functions: 90% Lines: 90%
Current results are comfortably above these; run npm run coverage
for the report.
npm run build validates the manifest, stamps the version from
package.json (the single source of truth), and writes both archives.
The build is reproducible: fixed timestamps, sorted entries, no
nondeterministic inputs — two builds of the same tree are
byte-identical.
options page ──> extension.storage <── content script
├── scan DOM
├── match text (whole-word)
├── pick smallest boundary
│ (p / heading / li / td / article)
└── add .word-filter-hidden
MutationObserver ──> scan added subtrees
| File | Role |
|---|---|
extension/matcher.js |
Whole-word, case-insensitive matching |
extension/filter.js |
DOM scan, content boundaries, hide/restore |
extension/content.js |
Orchestration: settings, observer, batching |
extension/config.js |
Defaults, normalisation, settings + stats persistence |
extension/compat.js |
browser/chrome + callback/promise wrapper |
extension/options.js |
Options page UI |
Filtering happens locally in the browser. The extension does not transmit page contents, URLs, browsing history, or filter results to a server.
There is no backend, no telemetry, no analytics, and no remote code.
The build fails if shipped sources gain a network call, eval, or
page-storage access.
The hide counter lives in local extension storage on your device — enough to show a total on the options page, nothing more.
| Permission | Why |
|---|---|
storage |
Persist your settings (enabled flag and word list). |
Content scripts on http://*/*, https://*/* |
The extension's purpose is to inspect and modify webpage DOM content. |
Not requested: tabs, cookies, browsing history, bookmarks,
downloads, account information, host permissions, network access.
See CONTRIBUTING.md. Keep it small, keep it local, avoid unnecessary dependencies.



