pywalfox: theming Firefox and Thunderbird with your Pywal palette
Dynamic theming of Firefox (and Thunderbird) using your Pywal colors
At a glance
- What is it?
- pywalfox is a browser add-on that reads the colours Pywal generated for your shell and applies them to Firefox and Thunderbird through Mozilla's Theme API, with a native messaging app that can refresh the theme on demand. It fits a Linux desktop where the terminal changes colour with the time of day, and it does not fit a system with no Pywal palette or no willingness to enable legacy user stylesheets.
- Who is it for?
- Adopt pywalfox if you already generate a Pywal palette and want Firefox and Thunderbird to follow it, especially if your theming script can call pywalfox update after a palette change. Skip it if you have no Pywal workflow, if your browser is sandboxed in a way that blocks the native messaging host, or if enabling toolkit.legacyUserProfileCustomizations.stylesheets is off the table for you.
- Can I use it commercially?
- Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 142 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two pieces: an add-on and a native messaging app
The premise of pywalfox is stated plainly in the project's own framing: Firefox and Thunderbird do not respect the Pywal colours the rest of the system uses, and this add-on fixes that. The implementation uses Mozilla's official Theme API rather than hacks, which matters because a supported API survives browser upgrades in a way that a patched profile does not.
The project is split across two repositories. This one holds the add-on itself, written in TypeScript, with extension/ and src/ directories, a rollup.config.js, a tsconfig.json and a yarn.lock at the top level. The companion native messaging application lives in a separate repository, Frewacom/pywalfox-native, and is distributed on PyPI as pywalfox.
That split is the architecture. The add-on cannot read your palette files directly, since a browser extension has no filesystem access. So it asks the native messaging application, which is a local process the browser is permitted to launch, and the native app does the file reading and the palette parsing. The native messaging manifest is what registers that permission, and it lives in your profile's native-messaging-hosts directory.
The consequence for you is that pywalfox is a two-part installation on every machine, and uninstalling means removing both.
Installing the native app and registering the manifest
The add-on itself is installed from the usual places, the Firefox add-ons site and the Thunderbird add-ons site. The native application is installed with pipx or pip, and the documentation gives one command:
pipx install pywalfoxIf pipx is missing, the documentation lists the per-platform package commands, pacman -S python-pipx on Arch Linux, apt install pipx on Ubuntu, brew install pipx on macOS, and on Windows the note is to install Python with winget and then run pip install pywalfox instead. The pipx route is preferred because it keeps the tool in its own environment rather than in your system Python.
Next, register the native messaging manifest with:
pywalfox installThen restart Firefox or Thunderbird. The ordering is not incidental, because the browser reads the manifest at startup.
Two environment cases need extra steps, and the documentation calls both out. Firefox forks such as LibreWolf require extra arguments, and Flatpaks require extra steps. Both come from the same root cause, which is that the browser has to be able to find and launch the native application on the host, and a sandboxed or forked browser may not look in the place the manifest points.
If something is wrong, the instruction is to read the Troubleshooting section before opening an issue, which is a reasonable expectation given how many ways the path from browser to host process can be broken.
Fetching the palette and refreshing it from a script
With the add-on installed and Pywal having generated a theme, the flow is short. The documentation lists generating a theme with Pywal or an equivalent, for example wal --theme gruvbox, then clicking the Pywalfox icon in the Firefox or Thunderbird interface and choosing Fetch Pywal colors.
The same thing can be done from a terminal:
pywalfox updateThe documentation says this command triggers an update of the browser theme and is functionally equivalent to clicking Fetch Pywal colors in the add-on settings GUI, and that it can integrate Pywalfox into system theming scripts. That is the feature that turns the add-on from a one-off into part of a setup: a script that regenerates the palette at sunrise and sunset can call pywalfox update, and the browser follows.
The setting that makes this work well is the palette template. The ordinary palette section is a temporary override for one or more colours from the Pywal palette, and the documentation warns that changes there are reset when you click Fetch Pywal colors or run pywalfox update. To make a customisation stick, you save the current palette as a palette template: fetch the colours, adjust them in the Palette section, load from current in the Palette template section, and save the palette. Colours chosen from the colourwheel rather than from the Pywal palette cannot be used in a template, and the same colours can also be set directly using Pywal colour indices.
Theme template, theme modes and the auto switch
Above the palette sits the theme template, which assigns colours from the palette template to individual browser elements. The documentation describes walking through the items in the Theme template section and assigning a colour to each, with the elements identified by the names shown in that section. The palette template says what colours exist, and the theme template says where each one goes.
Theme modes sit on top of that. There are three, Dark, Light and Auto, and Auto switches between the other two based on a time interval configured in the General section of the settings GUI. The detail that catches people is that dark and light have separate theme and palette templates, and you always modify the template for the mode currently selected.
That is a sensible design for a setup that follows a day and night cycle, and it is also where the configuration effort goes. A user who wants both modes to look right has to build two palette templates and two theme templates, and a user who only ever wants one mode can leave Auto alone.
Two optional extras are listed alongside the theming. DuckDuckGo searches can be themed automatically in Firefox, and there are options for bold text and styled dropdowns. The DuckDuckGo one is worth a note, because it changes how a search results page appears, which is a visible change beyond the browser chrome.
userChrome.css reaches what the Theme API cannot
Some browser elements are simply not available through the Theme API, the documentation gives context menus as the example. For those, pywalfox ships two custom stylesheets for Firefox, userChrome.css and userContent.css, which apply the theme to elements the API leaves alone.
There is a prerequisite and it is not optional. Before enabling the custom CSS sheets in the add-on settings GUI, you must navigate to about:config and set toolkit.legacyUserProfileCustomizations.stylesheets to true.
That is the most significant operational detail in the whole project, because it changes a browser setting that exists for compatibility with old profile customisations and is off by default in modern Firefox. It also makes the add-on's styling depend on a mechanism Firefox could change in a future release. A reasonable way to think about it is in two layers: the Theme API part is supported and low risk, and the stylesheet part is the part that gets you the elements the API cannot reach but that also carries the compatibility risk and the about:config requirement.
The order of operations follows from that. Try the theme with the API alone first, see which elements still look wrong, and only then decide whether the extra configuration is worth it.
Fork, Flatpak and uninstall paths
Two classes of environment break the default assumption that a browser can launch a local program.
Firefox forks need extra arguments, and Flatpaks need extra steps. The Flatpak case is the more interesting one, because a sandboxed browser cannot necessarily reach a binary installed on the host filesystem in the way a normally installed browser can. The documentation flags both without going into detail in the installation section, so if you run one of those configurations, expect to spend time on the native messaging host registration rather than on the theme itself.
Removal is documented in the same order as installation, which is the right way round. The native manifest comes out first:
pywalfox uninstall # Removes the manifest from native-messaging-hostsand then the application itself, with pipx uninstall pywalfox if that is how it was installed, or a package manager removal if it came from a distribution package such as the Arch Linux package python-pywalfox.
The add-on itself is removed through the browser's own add-on manager. Running the uninstall command first matters, because removing the add-on first leaves the manifest behind and the browser may still try to launch a process that no longer exists.
TypeScript, rollup and a build that runs its own checks
The add-on side is a TypeScript project with an unusual build script layout worth understanding if you want to contribute or fork it. The package.json defines a build script that runs a sequence: run-s build:* tests eslint web-ext-lint. The individual steps are build:compile with rollup, build:dist with web-ext build, and build:source which produces artifacts/source.zip from the extension, src, tsconfig.json, package.json, yarn.lock, rollup.config.js, LICENSE and README.md.
The consequence is that the full build only completes if the unit tests, the ESLint pass and the web-ext lint all pass. That is stricter than most browser extensions, and it is a reasonable choice for a project whose output is distributed through the add-on review process.
The tooling is current rather than dated. The dev dependencies include rollup 4 with the TypeScript and terser plugins, web-ext for running and linting the extension, jest 30 for tests, ESLint 8 with the airbnb-typescript config, TypeScript ESLint 7, and npm-run-all for the script sequencing. The type definitions for the WebExtension browser API are on version 143 of the Firefox types, which suggests the extension targets a current Firefox.
For day-to-day use there is also a debug workflow, with watch running rollup in watch mode and browser running web-ext run, and npm-run-all composing them into debug and debug:keep variants. That is the loop a developer uses while adjusting the theme templates.
The licence is MPL-2.0, the Mozilla Public License, which the repository badge points at and which is the same licence Firefox add-ons themselves use. It is a file-level copyleft: modifications to the covered files must be published under the same licence, while the add-on as a whole can be combined with other work. For a personal configuration tool that is unlikely to matter, and it keeps the project consistent with the platform it installs into.
Editorial conclusion
Adopt pywalfox if you already generate a Pywal palette and want Firefox and Thunderbird to follow it, especially if your theming script can call pywalfox update after a palette change. Skip it if you have no Pywal workflow, if your browser is sandboxed in a way that blocks the native messaging host, or if enabling toolkit.legacyUserProfileCustomizations.stylesheets is off the table for you. Verify first that the theme you get looks right with the Theme API alone, before turning on the custom stylesheets, since the extra CSS is what reaches the elements the API cannot.
Frequently asked questions
How do I install pywalfox?
Install the add-on from the Firefox add-ons site or the Thunderbird add-ons site, install the native messaging application with pipx install pywalfox, run pywalfox install to register the native messaging manifest, and restart the browser. Firefox forks such as LibreWolf need extra arguments and Flatpaks need extra steps.
How do I update the pywalfox theme from the terminal?
Run pywalfox update. It triggers an update of the browser theme and is functionally equivalent to clicking Fetch Pywal colors in the add-on settings GUI, which makes it usable from a system theming script.
Why do my pywalfox color changes disappear?
Changes made in the Palette section are temporary and are reset when you click Fetch Pywal colors or run pywalfox update. To make them persistent, save the current palette as a palette template using Load from current and Save palette, and note that colours picked from the colourwheel cannot go into a template.
What do I have to enable to use the custom CSS in pywalfox?
Before enabling the custom stylesheets in the add-on settings GUI, set toolkit.legacyUserProfileCustomizations.stylesheets to true in about:config. The userChrome.css and userContent.css files reach the browser elements that the Theme API does not expose, such as context menus.
How do I uninstall pywalfox?
Run pywalfox uninstall to remove the manifest from native-messaging-hosts, then remove the application with pipx uninstall pywalfox or with your package manager. The add-on itself is removed through the browser's add-on manager.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/frewacom-pywalfox)Was this useful?