2009-03-04

color theming

There are people who think it's a waste of time to spend time customizing the colors in emacs. They are probably right; I haven't seen evidence that the some color scheme makes you more productive (which is the purpose of life, of course). Red on orange may not be the best though. Nevertheless, I like to customize emacs' colors. I like darkness with shades of blue - please do your own psycho-analysis.

Anyway, to change the colors that emacs uses, you can do something like (yuk!)

(set-face-foreground 'default "blue")
(set-face-background 'default "green")
This sets the foreground color of the text recognized as default to blue, and the background to green; some other properties (such as 'underline') are available as well. Emacs 'knows' what text is supposed to be a 'warning', a 'variable' or a 'comment', and applies the color configured for it. Now, if you see some text that has some unexpected color, and you wonder why that is, move the cursor there, and call M-x describe-face. With that information, you can then configure the looks (note: if you don't see any colors you might be either (color-)blind, have a monochrome monitor, or forgot to specify (global-font-lock-mode t) in your .emacs)

You can also use set-face-foreground et al interactively; it lets you scroll to a long list of named colors, and alternatively you can use an HTML-style "#RRGGBB" hexadecimal red-green-blue color description. For example, "#000000" is black, "#ffffff" is white, and all colors of the form "#XYXYXY" (the RGB components are equal) give you various shades of gray: "#151515" is a rather dark gray, while "#e5e5e5" is rather light. For the full rainbow, a program like The Gimp can give you the hex-representation.

Using this knowledge, we can define all the colors, make comments gray, warnings red and so on. However, some people have already done much of the work for you, and prepared a wide range of color themes -- get the color-theme-el package. After installing, you can select interesting color-themes like M-x color-theme-subtle-hacker or M-x color-theme-blippblopp. Meanwhile, color-theme-select gives you an overview of all available color theme.

Of course, we're not happy with any of those, and prefer our very own theme. See below for an example of how to create your own color theme. When you're totally happy with the theme, you could even submit it to the color-theme people, and it might end up in a future version of the package.

(require 'color-theme)
(defun color-theme-djcb-dark ()
  "dark color theme created by djcb, Jan. 2009."
  (interactive)
  (color-theme-install
    '(color-theme-djcb-dark
       ((foreground-color . "#a9eadf")
         (background-color . "black") 
         (background-mode . dark))
       (bold ((t (:bold t))))
       (bold-italic ((t (:italic t :bold t))))
       (default ((t (nil))))
       
       (font-lock-builtin-face ((t (:italic t :foreground "#a96da0"))))
       (font-lock-comment-face ((t (:italic t :foreground "#bbbbbb"))))
       (font-lock-comment-delimiter-face ((t (:foreground "#666666"))))
       (font-lock-constant-face ((t (:bold t :foreground "#197b6e"))))
       (font-lock-doc-string-face ((t (:foreground "#3041c4"))))
       (font-lock-doc-face ((t (:foreground "gray"))))
       (font-lock-reference-face ((t (:foreground "white"))))
       (font-lock-function-name-face ((t (:foreground "#356da0"))))
       (font-lock-keyword-face ((t (:bold t :foreground "#bcf0f1"))))
       (font-lock-preprocessor-face ((t (:foreground "#e3ea94"))))
       (font-lock-string-face ((t (:foreground "#ffffff"))))
       (font-lock-type-face ((t (:bold t :foreground "#364498"))))
       (font-lock-variable-name-face ((t (:foreground "#7685de"))))
       (font-lock-warning-face ((t (:bold t :italic nil :underline nil 
                                     :foreground "yellow"))))
       (hl-line ((t (:background "#112233"))))
       (mode-line ((t (:foreground "#ffffff" :background "#333333"))))
       (region ((t (:foreground nil :background "#555555"))))
       (show-paren-match-face ((t (:bold t :foreground "#ffffff" 
                                    :background "#050505")))))))
To activate this theme, simply do M-x color-theme-djcb-dark. One nice side-effect of the color-theme package is the fact that colors work properly when you open new frames (windows).

23 comments:

Pierre said...

There is also the .Xdefault way to change emacs colors.

I only do basic customizations but noticed it's way faster than via .emacs and as a bonus, it allows remote X emacs to keep the same look - and since I am still running emacs 19 on some VMS box - color theme is not really an option...

this is my .Xdefault emacs part, tested from emacs 19 to 23 :-)
Should look like some kind of a poor man robin-hood theme...
! ================
! Emacs
! ================
Emacs.toolBar: False
Emacs.Foreground: wheat
Emacs.Background: darkslategrey
Emacs.menu.attributeForeground: wheat
Emacs.menu.attributeBackground: darkslategrey
!Emacs.cursorColor: Darkgrey
! modeline
Emacs.mode-line.attributeForeground: white
Emacs.mode-line.attributeBackground: grey15
Emacs.modeline.attributeForeground: white
Emacs.modeline.attributeBackground: grey15
! selection
Emacs.region.attributeBackground: lightblue
Emacs.region.attributeForeground: darkslategrey
Emacs.primary-selection.attributeBackground: blue
Emacs.secondary-selection.attributeBackground: darkslateblue
Emacs.show-paren-match-face.attributeBackground: Aquamarine
Emacs.show-paren-match-face.attributeForeground: SlateBlue
Emacs.show-paren-mismatch-face.attributeBackground: Red
Emacs.show-paren-mismatch-face.attributeForeground: White
Emacs.title: emacs

J. Aaron Farr said...

I really like the Tango theme:

http://www.emacswiki.org/cgi-bin/emacs/color-theme-tango.el

But what I really want is a theme that works for both the terminal and normal GUI frames.

djcb said...

@Pierre: yeah, that's a bit faster; I use Xdefaults for geometry, font and menu/toolbar on/off, which affect speed the most.

Anyway, with emacsclient (esp. in E23), the speed argument is getting less important

djcb said...

@J. Aaron Far: you could of course put something (untested!) like

----------------------------
(if (eq (symbol-value 'window-system) nil)
(color-theme-for-console)
(color-theme-tango))
-----------------------------------

in your .emacs. Good point about the console themes though; many themes that look nice in X are terrible in the console.

pra said...

To get a list of colors, M-x list-colors-display works for me. I get a nice list of 546 colors in a buffer to choose from.

Matthew said...

I'm running Emacs 23 in daemon mode. I'd like to use two different color themes depending on whether I run "emacsclient -t" (terminal frame) or "emacsclient -c -n" (X windows frame). If I weren't using daemon/emacsclient, it would be trivial to change the theme based upon the window-system value. Don't know how to do it though since .emacs evaluates window-system when "emacs --daemon" is run

Pierre said...

Something like this should do the trick:
(to update with your "theme" btw...)

;; Color themes
(require 'color-theme)
;; hook: test win sys to rerun ctheme
(defun test-win-sys(frame)
;; must be current for local ctheme
(select-frame frame)
;; test winsystem
(if (window-system frame)
(color-theme-gnome2)
(color-theme-tty-dark)
)
)
;; hook on after-make-frame-functions
(add-hook 'after-make-frame-functions 'test-win-sys)

;; default start - if non daemon
(let ((color-theme-is-global nil))
(if (window-system)
(color-theme-gnome2)
(color-theme-tty-dark)
)
)

Anonymous said...

> I haven't seen evidence that the some color
> scheme makes you more productive

Are you familiar with Zenburn? It's a low contrast theme originally created for Vim but it spread like fire. For me personally it was a life saver, I had headaches every single day... hard not to if you're spending 16 hours a day in front of the screen.

Anyway, you can the color-theme for Emacs and a bunch of other apps on it's home page http://slinky.imukuppi.org/zenburnpage/

Rod said...

Pierre's elisp code works, but if you have a terminal emacsclient open at the same time you have an X-windows emacsclient open, you always get the same color theme in both frames. That is, do emacsclient -c -n to open an X-windows frame. You get a frame with the gnome2 color theme. Then do emacsclient -t. You get a terminal frame with the tty-dark theme. However... the X-windows theme jumps to the tty-dark theme also.

I'm running emacs 23, using emacs --daemon to start the server. Is there any way around this?

Pierre said...

Hmm - guess I mixed up my test case...
I think this version should work better
in mixed case:
(note: I set gnome2 coz it makes my eyes bleed on console, so testing is obvious when it's not working... feel free to use another one...)

(require 'color-theme)
;; hook: test win sys and rerun color-theme
(defun test-win-sys(frame)
(let ((color-theme-is-global nil))
(select-frame frame)
(if (window-system frame)
(color-theme-gnome2)
(color-theme-tty-dark)
))
)
;; hook on after-make-frame-functions
(add-hook 'after-make-frame-functions 'test-win-sys)

;; default start
(let ((color-theme-is-global nil))
(if (window-system)
(color-theme-gnome2)
(color-theme-tty-dark)
)
)

;; side note: I

Steve Purcell said...

@Pierre, Rod & Matthew:

First, you can create two new separate hooks for the creation of window-system and TTY (console) frames:

(defvar after-make-console-frame-hooks '()
"Hooks to run after creating a new TTY frame")
(defvar after-make-window-system-frame-hooks '()
"Hooks to run after creating a new window-system frame")

(defun run-after-make-frame-hooks (frame)
"Selectively run either `after-make-console-frame-hooks' or
`after-make-window-system-frame-hooks'"
(select-frame frame)
(run-hooks (if window-system
'after-make-window-system-frame-hooks
'after-make-console-frame-hooks)))

(add-hook 'after-make-frame-functions 'run-after-make-frame-hooks)
(add-hook 'after-init-hook
(lambda ()
(run-after-make-frame-hooks (selected-frame))))


And then you can initialize your 2 themes using those hooks:


(set-variable 'color-theme-is-global nil)
(add-hook 'after-make-window-system-frame-hooks 'color-theme-vivid-chalk)
(add-hook 'after-make-console-frame-hooks 'color-theme-emacs-nw)



Works nicely for me!

Anonymous said...

Interesting discussion. I'm working on the same problem. The solution posted
by Pierre works fine, thanks a million! I only added a small change. I set
color-theme-is-cumulative to nil as well so the themes that are
loaded later don't contain remnants of a theme loaded earlier. Also, I put
the color-theme-related options outside the function so you can also change
the theme in a frame at some point without applying that change to all other
frames.

(defun apply-color-theme (frame)
(select-frame frame)
(if (window-system frame)
(color-theme-tango)
(color-theme-arjen)))

(setq color-theme-is-cumulative nil)
(setq color-theme-is-global nil)

(add-hook 'after-make-frame-functions 'apply-color-theme)

Anonymous said...

Now I know the downside of non-cumulativity: the standard are not used, so you have to set *every* face you use to a good value. Otherwise it's going to be 'default'.

chuckler said...

;; Color themes
(require 'color-theme)


(add-hook 'after-make-frame-functions
'(lambda (f)
(with-selected-frame f
(when (window-system f) (color-theme-comidia)))))

I was trying to use the above so as to not have my X-client theme interfere with the default theme.

Problem with the above is that if I invoke emacsclient -c ; though the theme gets applied correctly (color-theme-comidia) it also applies it to any running non-x client sessions.
Any way I can make sure it leaves the current terminal session theme alone and apply any themes mentioned above only for any X clients?

Faheem said...

(add-hook 'after-make-frame-functions
'(lambda (f)
(with-selected-frame f
(when (window-system f) (color-theme-comidia)))))

(setq color-theme-is-global nil)

Just needed to add the above line.
The above bit worked perfectly for my situation.
Thank you so much.
Brilliant Blog.

Anonymous said...

I recently discovered a page, where one can create a color-theme for emacs, vim, textmate ... there your can download an el-file, which uses color-them-mode to set the colors.

Anonymous said...

sorry, i forgot to mention the url to the site, where you can style your emacs_
http://inspiration.sweyla.com/code/

Anonymous said...

Thanks for this great article.

After I've set up my colors the way I like them, how do I then save the color theme itself? That is, how do I export and save the color theme file, or at least get the color theme source into a buffer so I can then save it.

djcb said...

@Anonymous: the easiest way might to take an existing color-theme (like zenburn, http://emacs-fu.blogspot.com/2010/04/zenburn-color-theme.html), change its name and make your changes.

Anonymous said...

@Anonymous: try M-x color-theme-print to dump out your current settings

Arnaud Meuret said...

I think you should really update the post to rip out the Gimp line and mention the list-colors-display function. AFAIK it has been around for a long time and must be available to pretty much everyone. Of course it also gives you the hex representation.

Anyway, I landed here while searching for the name of the face (color?) used for the window separators, still searching.

Anonymous said...

Zenburn advisor, you got my heart.
I can have same screen for both vim and emacs. Zuppa!

Drew said...

See also:
http://emacswiki.org/emacs/ColorTheme#toc4 for info about WYSIWYG cycling among themes.