TechEarl

How to Toggle Dark Mode From the macOS Command Line

Toggle macOS dark mode from the terminal with one osascript line: instant, no restart. Force it on or off, read the current state, and skip the dead _HIEnableThemeSwitchHotKey trick.

Ishan Karunaratne⏱️ 8 min readUpdated
Share thisCopied
Toggle macOS dark mode from the command line with a single osascript line: switch instantly, force on or off, and read the current state, no restart.

The command that actually toggles macOS dark mode from the terminal, instantly and with no restart, is one line of AppleScript run through osascript:

bash
osascript -e 'tell app "System Events" to tell appearance preferences to set dark mode to not dark mode'

Run it and the whole desktop flips: menu bar, Dock, Finder, and every app that respects the system appearance switch over the moment the line returns. not dark mode reads the current state and inverts it, so the same command toggles back and forth. There is no daemon to kick, no killall, no log out, and no reboot. It works on every macOS from Mojave (10.14, the release that introduced Dark Mode) through Tahoe.

If you found this page looking for the defaults write … _HIEnableThemeSwitchHotKey trick, skip it. That one does not switch the theme at all, and I explain why further down.

Force it on or force it off

not dark mode is great for a toggle bound to a hotkey, but for a script you usually want to set an explicit state rather than flip whatever happens to be current. Set the boolean directly:

bash
# Force Dark
osascript -e 'tell app "System Events" to tell appearance preferences to set dark mode to true'

# Force Light
osascript -e 'tell app "System Events" to tell appearance preferences to set dark mode to false'

Setting dark mode to true is the programmatic equivalent of clicking Dark in System Settings, Appearance; false is the same as clicking Light. These are idempotent: forcing Dark when you are already in Dark does nothing and does not error, which is exactly what you want in a script that runs on a schedule.

If you would rather drive it from JavaScript for Automation (JXA) than AppleScript, the same property is reachable that way and behaves identically:

bash
osascript -l JavaScript -e 'Application("System Events").appearancePreferences.darkMode = true'

It is the same System Events bridge under the hood, so it needs the same Automation grant and flips live with no restart. Pick whichever language your tooling already speaks.

Read the current state first

When a script needs to branch on the appearance (do one thing in Dark, another in Light), read the boolean instead of guessing:

bash
osascript -e 'tell app "System Events" to tell appearance preferences to get dark mode'

It prints true if Dark is active and false if Light is. That gives you a clean conditional in a shell script without parsing a plist:

bash
if [ "$(osascript -e 'tell app "System Events" to tell appearance preferences to get dark mode')" = "true" ]; then
  echo "Dark mode is on"
else
  echo "Dark mode is off"
fi

The first run asks for permission

The first time you run any of these, macOS pops an Automation consent dialog: "Terminal wants access to control System Events." Click OK and it never asks again for that terminal app. This is TCC (Transparency, Consent, and Control) doing its job, because the script is reaching into another process to change a system setting.

If you ever click the wrong button, or a script runs but the appearance does not change, the grant lives in System Settings, Privacy & Security, Automation. Find your terminal app in that list and make sure System Events is checked underneath it. A non-interactive runner (a launchd job, a cron line, a CI step) has to be pre-authorized there once, because there is no human present to click the dialog.

One thing that bites people after a big upgrade: macOS Sequoia (15) tightened TCC, and a major OS update can drop the existing Automation grant, so a toggle that worked yesterday silently does nothing until you re-approve System Events for your terminal. If a previously-working script goes quiet right after an update, that consent list is the first place to look.

Why the _HIEnableThemeSwitchHotKey advice is wrong

A trick that circulates widely tells you to run something like this:

bash
# This does NOT toggle the theme. Do not use it.
sudo defaults write /Library/Preferences/.GlobalPreferences.plist _HIEnableThemeSwitchHotKey -bool true

It does not switch your appearance. All it does is flip a private, long-dead preference that was meant to arm a keyboard hotkey for theme switching, and that hotkey has not functioned in current macOS for years. The dead giveaway is the instruction that almost always rides along with it: "you need to restart for it to take effect." The real appearance switch is instant. Any command that claims a reboot is required is touching the wrong key, because the genuine setting (appearance preferences set dark mode, above) flips live with no restart. It also asks for sudo and writes a system-wide plist, where dark mode is a per-user setting, another sign it is aimed at the wrong place. Use the osascript line.

A reusable toggle function

If you flip appearance often, wrap the toggle in a tiny shell function in your ~/.zshrc. The te_ prefix keeps it from colliding with anything else on your path:

bash
# Toggle macOS dark/light from the shell. No sudo, no restart.
te_darkmode() {
  osascript -e 'tell app "System Events" to tell appearance preferences to set dark mode to not dark mode'
}

Then te_darkmode flips the desktop from anywhere. Bind it to a global key with a tool like skhd if you want a single keystroke that genuinely works, unlike the dead hotkey above.

No sudo, because it is a per-user setting

None of these commands need sudo. Appearance is stored per user, so changing it is something your own session is allowed to do without elevation. If you find a snippet that reaches for sudo to set dark mode, treat that as another sign it is editing the wrong thing. Your account toggling its own look is not a privileged operation, the same way setting your own system volume does not need root either.

This is the same osascript and System Events bridge that drives a lot of macOS terminal automation, including setting the desktop wallpaper from the command line. It is worth getting comfortable with, because once you can read and set one appearance toggle, the same pattern controls a long list of UI settings that have no dedicated CLI.

See also

Sources

Authoritative references this article was fact-checked against.

TagsmacOSdark modeosascriptAppleScriptCLIterminalappearance

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

How to Turn Bluetooth On or Off From the macOS Command Line

Toggle Bluetooth on or off from the macOS terminal with blueutil: blueutil -p 1 / -p 0 / -p toggle, read the current state, list paired devices, and connect or disconnect by address. The reliable route now that Apple removed the old defaults hacks.