A zero-latency, native Pitch Black theme for GitHub.
Unlike style managers or JavaScript-based themes, this is a native browser extension: pure CSS, injected at document_start. No flash of unstyled content, no runtime overhead, no JavaScript execution at all.
- True Pitch Black — replaces GitHub's default dark tint (
#0d1117) with pure#000000 - Neutral Palette — desaturated borders, buttons, and text for a clean, distraction-free look
- High Contrast UI — lighter surfaces (
#161616) for inputs, dropdowns, and buttons to preserve visual hierarchy - Zero Latency — pure CSS content script, applied before first paint
- Privacy First — no JavaScript, no analytics, no network calls, no background processes
- 51 Languages — the listing and extension manager entry are localized; the theme itself works in any GitHub UI language
| Browser | Source |
|---|---|
| Chrome, Edge, Brave, Opera, Vivaldi, Arc | Chrome Web Store |
| Firefox | Firefox Add-ons |
| Safari | Build from source — not yet on the App Store |
git clone https://github.com/ztrahmet/github-theme-pitch-black.git
cd github-theme-pitch-black
npm run build:chrome # or build:firefox / build:safari / build:allEach target is built independently from the same src/ files into dist/<browser>/. Load it unpacked:
| Browser | Steps |
|---|---|
| Chrome / Edge / Brave / Opera / Vivaldi / Arc | chrome://extensions → enable Developer mode → Load unpacked → select dist/chrome |
| Firefox | about:debugging#/runtime/this-firefox → Load Temporary Add-on → select dist/firefox/manifest.json |
| Safari | requires macOS + Xcode: xcrun safari-web-extension-converter dist/safari, then build/run the generated project from Xcode |
Every push to main is packaged by GitHub Actions. Downloading an artifact gives you a zip containing the extension's own zip — GitHub always wraps artifacts — so unzip twice, then load the inner one as above.
- Edit
src/css/theme.css, the single source file shared by every browser build. - Run
npm installonce, then re-run the relevantnpm run build:*command. - Reload the extension from your browser's extension page and refresh GitHub.
src/css/theme.css single source of truth for the theme
src/icons/icon.svg icon artwork; everything else here is generated
src/_locales/<locale>/ extension name and description, 51 locales
scripts/icons.mjs regenerates the icon set from icon.svg
platforms/<browser>/ per-browser manifest.json (chrome, firefox, safari)
scripts/build.mjs assembles dist/<browser> from src/ + platforms/<browser>
preview.svg README preview, drawn from the palette
store-assets/ screenshots and store listing assets
dist/ build output (gitignored)
| Command | Output |
|---|---|
npm run build:chrome / build:firefox / build:safari / build:all |
Unpacked build in dist/<browser>/, for local testing |
npm run package:chrome / package:firefox / package:all |
Zipped store package in dist/artifacts/<browser>/*.zip |
npm run lint:firefox |
Runs web-ext lint against the Firefox build (AMO validation) |
npm run icons |
Regenerates src/icons/* from icon.svg; run after changing the artwork |
Safari has no package:safari script — the Mac App Store requires a signed, notarized Xcode archive rather than a .zip, so packaging there is the xcrun step above followed by a submission through Xcode/App Store Connect.
Only the name and description are translated. The theme is language-agnostic — every selector keys off structural attributes and Primer class names, never visible text — so GitHub's UI language never affects it.
Each locale is one file, src/_locales/<locale>/messages.json, defining extName and extDescription. scripts/build.mjs enforces what the stores require:
- Chrome's supported locale list. Chrome rejects codes outside it at upload; Firefox and Safari accept everything in it, so one list serves all three.
- Every locale defines every key, including the identical
extName. The browser would fall back to the default locale, but the Chrome Web Store rejects a package whose listing field is missing from any locale — andweb-ext lintdoes not catch it. - 132 characters per description, Chrome's cap, which several translations approach.
Translations are machine-written; only Turkish has been checked by a native speaker. Corrections are the easiest way to contribute — one file, and a complete pull request.
Safari's containing app takes its display name from InfoPlist.strings / CFBundleDisplayName instead, set during the xcrun step above.
Found a UI element that's still blue or lacks contrast? Spotted an awkward translation in your language? Contributions are welcome.
- Fork the project
- Create a feature branch (
git checkout -b feature/fix-button-color) - Commit your changes
- Push to the branch
- Open a pull request
Distributed under the MIT License. See LICENSE for details.