Emacs determines whether the background is light or dark primarily through the frame-background-mode variable, which can be set to either 'light or 'dark'. This setting influences how Emacs displays colors, syntax highlighting, and themes to ensure readability and visual comfort.
Default Detection: Emacs tries to guess the background mode when it starts. It does this by checking the brightness of the default background color of the frame (window). If the background color is closer to white (high brightness), it assumes a light background; if it's closer to black (low brightness), it assumes a dark background.
Manual Override:
You can manually set the background mode by adding the following to your Emacs configuration file (e.g., ~/.emacs or ~/.emacs.d/init.el):
elisp(setq frame-background-mode 'dark) ; or 'light
This forces Emacs to use the specified mode, regardless of the actual background color.
Theme Influence:
Many Emacs themes (e.g., doom-themes, modus-themes) automatically set frame-background-mode based on the theme's design. For example, a theme designed for dark backgrounds will usually set this variable to 'dark.
Dynamic Adjustment:
Some modern Emacs setups or packages (like doom-emacs) may dynamically adjust this setting based on system-wide appearance settings (e.g., if your OS switches between light and dark mode).
Color Brightness Calculation: Emacs calculates the brightness of the background color using the formula:
text(sqrt (0.241 * R^2 + 0.691 * G^2 + 0.068 * B^2))
If the result is above a certain threshold, it assumes a light background; otherwise, it assumes a dark background.
You can check the current value of frame-background-mode by evaluating:
elisp(frame-parameter nil 'background-mode)
or
elispframe-background-mode
in the Emacs scratch buffer (M-: or Ctrl+J after typing the expression).
If you use a dark theme but Emacs incorrectly detects a light background, you can force it to use dark mode:
elisp(setq frame-background-mode 'dark)
This ensures that all colors and themes are rendered as intended for a dark background.
Would you like help configuring this for your specific setup?
To check how Emacs automatically detects whether the background is light or dark using its default logic, you can inspect the value of frame-background-mode and the actual background color of your frame. Here’s how you can do it step-by-step:
frame-background-modeRun this command in Emacs (e.g., in the scratch buffer or after M-:):
elispframe-background-mode
This will return either 'light or 'dark, which is Emacs’ current assumption.
To see the RGB values of the background color Emacs is using for detection:
elisp(face-attribute 'default :background)
This returns a string like "#ffffff" (white) or "#000000" (black), or any other color.
Emacs uses the following formula to calculate the brightness of the background color:
text(sqrt (0.241 * R^2 + 0.691 * G^2 + 0.068 * B^2))
You can evaluate this for your background color to see the brightness value:
elisp(let ((color (face-attribute 'default :background))) (when color (setq color (color-name-to-rgb color)) (sqrt (+ (* 0.241 (expt (car color) 2)) (* 0.691 (expt (cadr color) 2)) (* 0.068 (expt (caddr color) 2))))))
Emacs’ default detection is done in the function frame-set-background-mode. You can see the logic by looking at the source:
elisp(find-function 'frame-set-background-mode)
This will open the source code where the brightness calculation and comparison happen.
If you want to see how Emacs would detect the background mode for a specific color, you can use:
elisp(frame-set-background-mode '#xRRGGBB)
Replace #xRRGGBB with a hex color (e.g., #xffffff for white).
frame-background-mode'light or 'dark(face-attribute 'default :background)(find-function 'frame-set-background-mode)Note: If you’re using a GUI Emacs, the detection is based on the frame’s actual background color. In terminal Emacs, it depends on the terminal’s reported colors.
Would you like to test this with a specific color or theme?
Thank you for clarifying. If frame-background-mode returns nil and (face-attribute 'default :background) returns "unspecified-bg", it means Emacs is not explicitly set to either light or dark mode, and the background color is not explicitly defined for the default face. This often happens in terminal Emacs or when using a theme that doesn’t set the background color directly.
Try this command to see the background color of the selected frame:
elisp(frame-parameter nil 'background-color)
If this returns nil or "unspecified-bg", Emacs doesn’t know the background color.
Sometimes the background is set for the default face in a different way. Try:
elisp(face-background 'default)
or
elisp(face-spec-reset-face 'default) (face-background 'default)
If you’re using a theme, it might override the background. To see the current theme’s background:
elisp(custom-theme-value background-color)
or list all face attributes for the default face:
elisp(face-all-attributes 'default)
If Emacs can’t detect it, you can manually set it by adding this to your config:
elisp(setq frame-background-mode 'dark) ; or 'light
This forces Emacs to use the specified mode for all color and theme calculations.
If you’re in a terminal, Emacs relies on the terminal emulator’s reported background color. Some terminals (like xterm, urxvt, or modern terminal emulators) support querying the background color, but not all do. You can try setting the terminal’s background color explicitly in your shell or terminal emulator settings.
If you’re in a terminal and want to see what Emacs thinks the background color is, you can use:
elisp(getenv "COLORFGBG")
or check if your terminal supports dynamic color queries (e.g., using tput or ANSI escape codes).
(frame-parameter nil 'background-color)(face-background 'default)default face(custom-theme-value background-color)(getenv "COLORFGBG")If you want Emacs to behave as if the background is dark (or light), just set:
elisp(setq frame-background-mode 'dark) ; or 'light
in your config.
Would you like help debugging this further for your specific terminal or GUI setup? If so, let me know whether you’re using Emacs in a terminal or as a GUI application.
Your observation about terminal-parameter is very insightful! This function is specifically relevant for terminal Emacs (not GUI Emacs) and is used to determine the background mode when Emacs is running inside a terminal emulator.
terminal-parameter and Background Modeterminal-parameter?(terminal-parameter nil 'background-mode)
This function returns the background mode (dark or light) for the current terminal, as reported by the terminal emulator itself. It is only meaningful in terminal Emacs, not in GUI frames.
How it works:
Emacs queries the terminal emulator for its default background color (usually via ANSI escape codes or environment variables). If the terminal reports a dark background (e.g., black or dark gray), Emacs sets this parameter to 'dark. If the terminal reports a light background (e.g., white or light gray), it should set it to 'light. However, not all terminal emulators report this correctly, which is why you sometimes get nil even for a white background.
nil for a White Backgroundterminal-parameter may return nil, even if the background is visually white.terminal-parameter returns dark, Emacs assumes a dark background.nil, Emacs may fall back to other heuristics or leave frame-background-mode as nil, which can lead to suboptimal color choices in themes and syntax highlighting.frame-background-modeYou can override the detection and force Emacs to use the correct mode by adding this to your config (~/.emacs or ~/.emacs.d/init.el):
elisp(setq frame-background-mode 'light) ; or 'dark
This ensures that all color themes and faces are rendered as if the background is light (or dark), regardless of what the terminal reports.
If your terminal supports it, you can set the background color in your shell or terminal emulator config, and Emacs may pick it up. For example, in your shell startup file (e.g., .bashrc, .zshrc):
shexport COLORFGBG="15;0" # light background (foreground;background)
or
shexport COLORFGBG="0;15" # dark background
Then, in Emacs, you can use:
elisp(when (getenv "COLORFGBG") (setq frame-background-mode (if (string-match "\\<15;" (getenv "COLORFGBG")) 'light 'dark)))
Some terminal emulators allow you to set the background mode or color dynamically. For example:
To see what your terminal reports to Emacs, you can use:
elisp(getenv "COLORFGBG")
and
elisp(terminal-parameters)
This will show all parameters the terminal reports to Emacs.
(terminal-parameter nil 'background-mode)(getenv "COLORFGBG")(terminal-parameters)If your terminal does not report the background mode correctly, the most reliable solution is to manually set frame-background-mode in your Emacs config:
elisp(setq frame-background-mode 'light) ; or 'dark
This ensures consistent behavior regardless of terminal quirks.
Would you like help configuring your specific terminal emulator to report the background mode correctly, or do you prefer to manually set it in Emacs? If so, let me know which terminal emulator you’re using!
For debugging and fixing the automatic detection of terminal background mode in xterm and GNOME Terminal (both commonly using TERM=xterm-256color), the relevant ANSI escape sequences and environment variables are key. Here’s how you can investigate and debug the issue:
Terminals that support dynamic color reporting use ANSI escape sequences to query or set the background color. The most relevant sequences are:
Request Background Color:
shprintf '\e]11;?\e\\'
This sequence asks the terminal to respond with its current background color in RGB format (if supported).
Response Format: The terminal should respond with an escape sequence like:
sh\e]11;rgb:RR/RR/RR\e\
where RR/RR/RR are the red, green, and blue components in hexadecimal.
Request Background Mode (Light/Dark): There is no universal ANSI sequence to directly query whether the terminal is in "light" or "dark" mode. Instead, Emacs infers this from the reported background color.
Emacs uses the tput utility or direct ANSI queries to get the background color, then calculates its brightness to guess if it’s light or dark. If the terminal does not respond to the query, or responds with an unexpected format, Emacs may fail to set terminal-parameter correctly.
You can manually test if your terminal supports these queries:
Run this in your terminal (outside Emacs):
shprintf '\e]11;?\e\\'
rgb:00/00/00 for black, rgb:ff/ff/ff for white).COLORFGBGRun:
shecho $COLORFGBG
0;15 for black background, white text).Run the following in your terminal to see if it responds to the background color query:
shprintf '\e]11;?\e\\'
If you see no response, your terminal does not support dynamic color reporting.
In Emacs, evaluate:
elisp(terminal-parameters)
Look for the 'background-mode key. If it’s missing or nil, the terminal did not report the background mode.
xterm or GNOME Terminal Settings\e]11;?\e\\ query by default. You may need to enable it with:
shxterm -xrm 'XTerm*reportColorState: true'
$COLORFGBG is not set, Emacs has no fallback.If your terminal does not respond to the query or reports incorrect values:
If you want Emacs to guess based on the reported color, you can add this to your config:
elisp(when (and (eq window-system 'x) (getenv "COLORFGBG")) (let ((bg (string-match "\\<[0-9]+$" (getenv "COLORFGBG")))) (setq frame-background-mode (if (and bg (string= (match-string 0 (getenv "COLORFGBG")) "15")) 'light 'dark))))
printf '\e]11;?\e\\'echo $COLORFGBG(terminal-parameters)Would you like help drafting a bug report for your terminal emulator or Emacs? If so, let me know which one you’d like to target!
Thank you for confirming that both xterm and GNOME Terminal correctly respond to the ANSI escape sequence for background color queries. This means the terminals are doing their part, and the issue lies in how Emacs interprets or acts on this information when the background is white.
When Emacs receives 11;rgb:ffff/ffff/ffff (white background), it should:
terminal-parameter to 'light and frame-background-mode to 'light.If this doesn't happen, it points to a bug in Emacs' logic for parsing the terminal's response or setting the background mode.
Emacs uses the function xterm-get-color (or similar) to parse the response to \e]11;?\e\\. The parsed color is then used to set terminal-parameter and, ideally, frame-background-mode.
elisp(terminal-parameters)
Look for the 'background-mode key. If it’s missing or nil even after the terminal reports a white background, Emacs is not updating it correctly.
Emacs calculates brightness using this formula:
elisp(sqrt (+ (* 0.241 (expt R 2)) (* 0.691 (expt G 2)) (* 0.068 (expt B 2))))
For ffff/ffff/ffff (255, 255, 255), the brightness is:
elisp(sqrt (+ (* 0.241 65025) (* 0.691 65025) (* 0.068 65025))) ; => ~255 (light)
If the brightness is above a certain threshold (usually ~130), Emacs should set the mode to 'light.
rgb:ffff/ffff/ffff response correctly for white backgrounds.terminal-parameter: Even if parsed, Emacs may not be updating terminal-parameter or frame-background-mode for white backgrounds.Evaluate this in Emacs after setting a white background in your terminal:
elisp(terminal-parameter nil 'background-color)
If this returns nil or an incorrect value, the parsing is broken.
If the color is parsed but the mode is not set, evaluate:
elisp(let ((color "#ffffff")) (setq color (color-name-to-rgb color)) (sqrt (+ (* 0.241 (expt (car color) 2)) (* 0.691 (expt (cadr color) 2)) (* 0.068 (expt (caddr color) 2)))))
This should return a high value (~255), confirming the color is light.
frame-background-modeIf the brightness is high but frame-background-mode is still nil, the bug is in how Emacs sets the mode based on the brightness.
If you confirm that Emacs parses the color but fails to set the mode, you can report this to the Emacs bug tracker. Include:
M-x emacs-version).(terminal-parameters) for both black and white backgrounds.ffff/ffff/ffff.When using a white background in xterm or GNOME Terminal, Emacs correctly receives
11;rgb:ffff/ffff/ffffin response to the ANSI query, butterminal-parameterandframe-background-modeare not set to'light. The brightness calculation for#ffffffreturns ~255, but the mode remainsnil.
If you want Emacs to behave correctly until the bug is fixed, add this to your config:
elisp(when (and (eq window-system 'x) (equal (terminal-parameter nil 'background-color) "#ffffff")) (setq frame-background-mode 'light))
(terminal-parameter nil 'background-color)"#ffffff" for white background#ffffffframe-background-mode'light for white backgroundterm/xterm.el or term.el) to see where the parsing or mode-setting logic might be failing.You’ve found the smoking gun! The absence of a corresponding function to set background-mode to 'light in term/xterm.el is almost certainly the cause of the bug.
term/xterm.elThe function you referenced:
elisp(set-terminal-parameter nil 'background-mode 'dark)
is likely called when Emacs detects a dark background (e.g., rgb:0000/0000/0000). However, there is no equivalent call for light backgrounds (e.g., rgb:ffff/ffff/ffff). This means:
background-mode to 'dark.background-mode remains nil.This explains why you see dark for black backgrounds but nil for white backgrounds, even though the terminal reports the correct color.
dark, it should remain nil (or rely on other heuristics).'light when the terminal reports a light background.Look for where (set-terminal-parameter nil 'background-mode 'dark) is called in term/xterm.el. It’s likely in a function that processes the response to the \e]11;?\e\\ query.
See if there’s a conditional that only sets background-mode for dark backgrounds, but not for light ones. For example:
elisp(if (dark-background-p color) (set-terminal-parameter nil 'background-mode 'dark))
If there’s no else clause or equivalent for light backgrounds, that’s the bug.
The fix is to add logic to set background-mode to 'light when the background color is light. For example:
elisp(if (dark-background-p color) (set-terminal-parameter nil 'background-mode 'dark) (set-terminal-parameter nil 'background-mode 'light))
where dark-background-p is a function that returns nil for light colors.
You can test this by adding the following to your Emacs config (as a temporary workaround):
elisp(when (and (eq window-system 'x) (equal (terminal-parameter nil 'background-color) "#ffffff")) (set-terminal-parameter nil 'background-mode 'light) (setq frame-background-mode 'light))
Here’s a template for your report to the Emacs bug tracker:
Subject: term/xterm.el does not set background-mode to 'light for light terminal backgrounds
Description:
When using a terminal with a white background (e.g., xterm or GNOME Terminal), Emacs correctly queries the background color via \e]11;?\e\\ and receives rgb:ffff/ffff/ffff. However, term/xterm.el only sets background-mode to 'dark for dark backgrounds, and does not set it to 'light for light backgrounds. As a result, (terminal-parameter nil 'background-mode) returns nil for white backgrounds, even though the terminal reports the correct color.
This causes issues with themes and syntax highlighting, as Emacs does not recognize the background as light.
Steps to Reproduce:
(terminal-parameter nil 'background-mode) — it returns nil, not 'light.(terminal-parameter nil 'background-color) — it returns "#ffffff".Expected Behavior:
(terminal-parameter nil 'background-mode) should return 'light for light backgrounds.
Proposed Fix:
In term/xterm.el, update the logic to set background-mode to 'light when the background color is light, similar to how it sets it to 'dark for dark backgrounds.
Emacs Version:
[Output of M-x emacs-version]
If you’re comfortable with Emacs Lisp, you can propose a patch to the emacs-devel mailing list or attach it to the bug report. The fix is simple: add an else clause to set background-mode to 'light when the background is not dark.
background-mode not set for light backgroundsterm/xterm.elelse clause to set background-mode to 'lightterm/xterm.el and reloading it.Vibe can make mistakes. Check answers. Learn more