ELisp: How to Define Face

By Xah Lee. Date: . Last updated: .

What is Face

A face is graphical attributes for displaying text, such as text color, size, font, etc.

In emacs major mode, typically you use higher-level font-lock-mode system to color your language words. Basically, just assign a list to the variable font-lock-defaults.

For example:

;; a simple major mode, mymath-mode

(setq mymath-highlights
      '(("Sin\\|Cos\\|Sum" . 'font-lock-function-name-face)
        ("Pi\\|Infinity" . 'font-lock-constant-face)))

(define-derived-mode mymath-mode prog-mode "mymath"
  "major mode for editing mymath language code."
  (setq font-lock-defaults '(mymath-highlights)))

[see ELisp: Write a Major Mode for Syntax Coloring]

in the code above, the font-lock-function-name-face and font-lock-constant-face are predefined faces.

List Faces

To list all loaded faces, Alt+x list-faces-display.

emacs list-faces-display 2021-08-26
emacs list-faces-display

font-lock-mode Faces

The following are faces defined by font-lock-mode.

  1. font-lock-builtin-face
  2. font-lock-comment-delimiter-face
  3. font-lock-comment-face
  4. font-lock-constant-face
  5. font-lock-doc-face
  6. font-lock-doc-markup-face
  7. font-lock-function-name-face
  8. font-lock-keyword-face
  9. font-lock-negation-char-face
  10. font-lock-preprocessor-face
  11. font-lock-string-face
  12. font-lock-type-face
  13. font-lock-variable-name-face
  14. font-lock-warning-face

If you are creating a programing language mode, use these face as much as possible, because that will create consistent style of coloring across programing language modes.

Predefined Faces in Emacs

The following are basic faces defined by emacs.

Define Face

To define a face, use defface.

;; examples of defining faces

(defface my-lang-phi-word
  '((t :foreground "black"
       :background "aquamarine"
       :weight bold
       :underline t
  "Face for function parameters."
  :group 'my-lang-mode )

(defface my-lang-gamma-word
  '((t :foreground "red"
       :background "#f5f5f5"
  "Face for global variables."
  :group 'my-lang-mode )

You can use the above code as a template to define your faces.

Alt+x list-colors-display to list named colors and their hexadecimal values.

Emacs's face system supports terminal emulators that has limited colors. For example, you can define a face such that when user is in a terminal that only has 8 colors, the face will use a available color and still sensible.

For example, here's the definition of the standard face highlight:

(defface highlight
  '((((class color) (min-colors 88) (background light))
     :background "darkseagreen2")
    (((class color) (min-colors 88) (background dark))
     :background "darkolivegreen")
    (((class color) (min-colors 16) (background light))
     :background "darkseagreen2")
    (((class color) (min-colors 16) (background dark))
     :background "darkolivegreen")
    (((class color) (min-colors 8))
     :background "green" :foreground "black")
    (t :inverse-video t))
  "Basic face for highlighting."
  :group 'basic-faces)

Note: elisp manual says face name should not end in “-face” and reason being “redundant”.

Defining Faces (ELISP Manual)

Face Attributes (styles)

You can specify font, size, weight, text color, background color, underline, overline, border, slant (italic), etc. To see complete list of attributes, see: Face Attributes (ELISP Manual)

Redefine Face

When you are working on major mode, often you need to experiment on which color/face is best.

defface won't set the face when a face name already has a face spec. (that is, when you change a face's spec and re-eval the buffer, your new spec has no effect.)

Use face-spec-set to force set a face spec. (defface is like defvar, and face-spec-set is like setq.)

(defface my-identifier-x
  '((t :foreground "red"
      :weight bold
  "face for user defined variables."
  :group 'my-mode )

 '((t :foreground "blue"
      :weight bold

A Named Face is Not a Variable

Note: A named face is not a variable. defface does not create a new variable. In elisp technicality, defface does not set the symbol's value cell. (boundp 'face_name) returns nil.

A named face (such as those created by defface) is specified by setting the face-defface-spec property name of the symbol's property list. [see ELisp: Symbol] [see ELisp: Symbol Property List]

You can use defvar to make a face_name symbol also a variable, but that is not necessary.

Those faces predefined from font-lock-mode, such as font-lock-function-name-face, are both named faces and variables.

Check is Face or is Variable

Return true is its a face.
Return true is its a variable.
;; example of user defined face

(defface my-great-face
  '((t :foreground "red"))
  "my face"

;; check if a symbol is a variable. that is, value cell is not void
(boundp 'my-great-face) ; nil

;; check if a symbol is a face
(facep 'my-great-face) ; non-nil

;; get the value of 'face-defface-spec from symbol's plist
(get 'my-great-face 'face-defface-spec ) ; ((t :foreground "red" :weight bold))

;; now make it a variable. (you shouldn't do this)
(defvar my-great-face nil "my face too")

(boundp 'my-great-face) ; t

See also: ELisp: Symbol

Emacs Face