NewsInverter flips the meaning of headlines, sub-headlines, and article text without ever flashing the original text.
It achieves this with an ultra-early prehide CSS (runs at document_start) + headline overlays rendered via ::before.
FOOC = Flash Of Original Content — this project drives FOOC to zero.
- Features
- How it works
- Project structure
- Installation (Load Unpacked)
- Manifest (MV3) example
- Critical prehide CSS
- Headline overlay style
- Service Worker contract
- Config knobs
- Debugging checklist
- Limitations
- Roadmap
- License
- No original text visible, ever — prehide targets only not-yet-inverted nodes.
- Headline overlay — original headline is transparent; visible text is drawn via
::before. - Paragraph replacement — body text replaced in DOM with the inverted version.
- SPA/lazy content ready — re-inverts on DOM mutations and when elements enter viewport.
- Fingerprint memory — repeated original snippets are auto-replaced from cache.
- LLM + local fallback — calls your backend/LLM; if it times out, uses a quick antonymizer.
- Defensive CSS — never hides containers or pseudo-elements, so overlays stay visible.
-
content_start.js(runs atdocument_start)- Immediately sets
html[data-ni-prehide="1"]and injects prehide CSS to hide only non-inverted nodes. - Injects
page_inject.jsinto the page. - Bridges
window.postMessage⇄ Service Worker (action: "invert").
- Immediately sets
-
page_inject.js(in-page logic)- Finds the article root, collects
H1/H2/H3,P,LI,BLOCKQUOTE,FIGCAPTION, etc. - Headlines: apply overlay (
::before) + mark withdata-news-inverted="1". - Paragraphs: replace
textContent+ mark withdata-news-inverted="1". - Guards:
MutationObserver+IntersectionObserverkeep everything inverted on SPA updates. - Fingerprinting: normalized original → inverted cache for instant re-apply.
- Finds the article root, collects
-
Service Worker
- Responds to
{ action: "invert", text }with{ ok: true, text: "<inverted>" }. - (Optional) handles
inject_page_scriptfallback.
- Responds to
Why zero FOOC?
Nodes are transparent until inverted (prehide selectors). As soon as inversion is applied, we set data-news-inverted="1" and (for headlines) show overlay text via ::before while the original node stays transparent.
/extension ├─ manifest.json ├─ content_start.js # prehide + SW bridge + injects page_inject.js (document_start) ├─ page_inject.js # selectors, inversion logic, overlays, guards, fingerprinting └─ service_worker.js # responds to 'invert', optional 'inject_page_script'
- Go to
chrome://extensionsand enable Developer mode. - Click Load unpacked and select the
/extensionfolder. - Open a news article page — you should see only inverted text (no original flash).
{
"name": "NewsInverter",
"version": "0.1.0",
"manifest_version": 3,
"permissions": ["scripting", "storage"],
"host_permissions": ["<all_urls>"],
"background": { "service_worker": "service_worker.js" },
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content_start.js"],
"run_at": "document_start",
"all_frames": true
}],
"web_accessible_resources": [{
"resources": ["page_inject.js"],
"matches": ["<all_urls>"]
}]
}Text drawn in or images can’t be inverted.
Exotic masking might confuse color detection (fallbacks exist).
Highly dynamic/obfuscated pages may need per-site selector tweaks.
Persistent fingerprint cache (SW side).
Per-site selector presets and heuristics.
Options page (domain toggles, thresholds).
Streaming/partial overlays for very long blocks.
MIT — do what you want, no warranty.