Most theme families these days ship both light and dark variants. For example,Tokyo Themes has tokyo-day(light) alongside tokyo-night, tokyo-storm, and tokyo-moon (all dark).Batppuccin has batppuccin-latte(light) and batppuccin-mocha, batppuccin-macchiato, batppuccin-frappe(dark). But switching between them manually gets old fast. Here are a few waysto automate it.Following the OS Appearance (macOS)Since Emacs 29, the macOS (aka ns/NextStep port) build can detect when the OSswitches between light and dark mode. The hookns-system-appearance-change-functions fires whenever that happens, passing thesymbol light or dark as an argument. All you need is:(defun my-apply-theme (appearance) "Load theme based on APPEARANCE (light or dark)." (mapc #'disable-theme custom-enabled-themes) (pcase appearance ('light (load-theme 'tokyo-day t)) ('dark (load-theme 'tokyo-night t))))(add-hook 'ns-system-appearance-change-functions #'my-apply-theme)That’s it. When you flip the system appearance (or the OS does it automaticallybased on time of day), Emacs follows along. The mapc line disables anycurrently active themes first – without it, load-theme stacks the new themeon top of the old one, which can cause weird color bleed between the two.This approach is nice because it keeps Emacs in sync with every other app onyour system. If you’re on macOS and running Emacs 29+, this is probably what youwant.Note: This only works with the native macOS build. If you’re running Emacsunder X11 on macOS or on Linux/Windows, read on.Following the OS Appearance (Cross-Platform)If you want OS appearance tracking without writing the hook yourself, or youneed it on a platform other than macOS, check outauto-dark. It detects OS-leveldark/light mode changes on macOS, Linux (via D-Bus/GNOME), Windows, and evenAndroid (Termux):(use-package auto-dark :ensure t :config (setq auto-dark-themes '((batppuccin-mocha) (batppuccin-latte))) (auto-dark-mode))The value is a list of two lists – dark theme(s) first, light theme(s) second.The extra nesting is there because you can stack multiple themes per mode (e.g.,a base theme plus an overlay). For a single theme per mode, the format above isall you need. auto-dark polls the system appearance every few seconds andswitches accordingly. It also provides auto-dark-dark-mode-hook andauto-dark-light-mode-hook if you want to run extra code on each switch.Time-Based Switching with circadian.elIf you want theme switching based on time of day regardless of your OS, take alook at circadian.el. It canswitch themes at fixed times or based on your local sunrise/sunset:(use-package circadian :ensure t :config (setq circadian-themes '((:sunrise . batppuccin-latte) (:sunset . batppuccin-mocha))) (circadian-setup))You can also use fixed hours if you prefer:(setq circadian-themes '(("8:00" . batppuccin-latte) ("20:00" . batppuccin-mocha)))For sunrise/sunset to work, set calendar-latitude and calendar-longitude inyour config. circadian.el uses Emacs’s built-in solar calculations, so noexternal services are needed.1Rolling Your Own with run-at-timeIf you don’t want an extra dependency, you can do something basic withrun-at-time:(defun my-set-theme-for-time () "Switch theme based on current hour." (let ((hour (string-to-number (format-time-string "%H")))) (mapc #'disable-theme custom-enabled-themes) (if (