Search by

raneomik / tracy-dark-mode

raneomik

Dark theme for the Tracy bar, its panels and the bluescreen, with a toggle

Package info

github.com/raneomik/tracy-dark-mode

pkg:composer/raneomik/tracy-dark-mode

Statistics

Installs: 10

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-11-10 01:21 UTC

This package is auto-updated.

Last update: 2026-11-11 01:59:58 UTC


README

🌗 Dark theme for the Tracy bar, its panels (third-party ones too) and the bluescreen.

It follows the system (prefers-color-scheme); the half-moon tab of the bar switches the theme and remembers the choice.

Requirements

  • PHP 8.3 or newer
  • Tracy 2.11.4 and above (the bar and the bluescreen in shadow roots: supported)
  • Optional: nette/di 3.2 or newer, to enable it with one line of NEON

Getting started

Install the package:

composer require --dev raneomik/tracy-dark-mode

With Nette, register the extension:

extensions:
    tracyDarkMode: Raneomik\TracyDarkMode\DarkTracyExtension

Reload a page in debug mode: the bar is dark if your system is, and a 🌗 half-moon tab sits at its right end. Click it to switch between dark and light.

⚠️ Installed with --dev, the class is missing after composer install --no-dev: registered in your common config, the extension breaks the production container. See Keep it out of production.

How-to guides

Keep it out of production

Register the extensions of your require-dev packages in a config file of their own:

# config/dev.neon
extensions:
    tracyDarkMode: Raneomik\TracyDarkMode\DarkTracyExtension

Load it only when Composer installed the dev packages:

// app/Bootstrap.php
if (Composer\InstalledVersions::getRootPackage()['dev']) { // false after composer install --no-dev. "%rootDir" may need definition
    $configurator->addConfig('%rootDir%/config/dev.neon');
}

The $customCssFiles line of the next guide goes in the same condition.

Theme the early bluescreens and the panels opened in a window

The extension themes the bar, its panels and the bluescreens raised once the container has started. Tracy serves its own assets as soon as it is enabled, before any DI extension runs: to also cover a bluescreen raised earlier and a panel opened in its own window, add the stylesheet before enableTracy().

// app/Bootstrap.php
Tracy\Debugger::$customCssFiles['darkMode'] = __DIR__ . '/../vendor/raneomik/tracy-dark-mode/assets/dark-mode.css';

$configurator->enableTracy(__DIR__ . '/../log');

Use an absolute path: a relative one is resolved from the working directory, which is the document root in a web request.

Use it without Nette

The stylesheet line above and the toggle are all it takes:

Tracy\Debugger::$customCssFiles['darkMode'] = __DIR__ . '/vendor/raneomik/tracy-dark-mode/assets/dark-mode.css';
Tracy\Debugger::getBar()->addPanel(new Raneomik\TracyDarkMode\DarkModePanel, 'darkMode');

Go back to the system theme

A click on the toggle is remembered, and wins over the system from then on. To follow the system again, remove the stored choice in the browser console, then reload the page:

localStorage.removeItem('tracy-dark-mode');

Reference

Item Description
Raneomik\TracyDarkMode\DarkTracyExtension DI extension, no option. Registers the stylesheet at container startup and adds the toggle to the bar when Tracy's extension is registered.
Raneomik\TracyDarkMode\DarkModePanel Bar panel: the toggle tab, with the stylesheet and the script inlined. It has no panel content, and no tab in AJAX requests.
assets/dark-mode.css The theme. To pass to Tracy\Debugger::$customCssFiles for what the panel cannot reach.
assets/dark-mode.js The toggle, mouse and keyboard (Enter, Space). Inlined by the panel, nothing to load yourself.
localStorage['tracy-dark-mode'] dark or light once the toggle was clicked; absent, the system decides. When the storage is blocked, the choice lasts for the page only.
--tracy-dark-filter, --tracy-dark-undo, --tracy-dark-bg Custom properties set on <html> by the toggle.

How it works

Tracy's colours are hard-coded, in third-party panels too. Instead of recolouring them one by one, the bar, each panel and the bluescreen are inverted as a whole, hues kept (filter: invert() hue-rotate()): no panel needs to know about it.

What must keep its own colours is inverted back: pictures and the dumps that are already dark (tracy: dumpTheme: dark). The red error header of the bluescreen is left untouched.

The choice made with the toggle is written as custom properties on <html>: they inherit into the shadow roots of Tracy 2.13, where no selector of the page reaches.

Development

composer install && composer ci   # PHPStan + tests/smoke.php

Resources

License

MIT