I found this piece of code to change org-mode list elements from dashes (-) to circles (•):

(font-lock-add-keywords 'org-mode
                          '(("^ *\\([-]\\) "
                             (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))

It works wonderfully, but I cannot figure out how to change the color of the circle to red. I tried reading the documentation of font-lock-add-keywords, but the syntax seems quite cryptic to me. Any help, pointers towards more information, or suggestions on how to achieve what I want would be most welcome.


Based on NickD's comment and on a blog post by Kitchin on colorizing strings, I ended up with the following:

   '(("^ *\\([-]\\) "
      (0 (prog1 () (compose-region
                    (match-beginning 1)
                    (match-end 1)
                    (propertize "•" 'font-lock-face '(:foreground "red"))))))))

But that doesn't seem to do the trick. I guess the compose-region function must be ignoring any text properties, as inserting the propertized string directly in the buffer does work.

Any help is highly appreciated.

  • Add properties to the "•" string - see e.g. this question.
    – NickD
    Jul 29, 2020 at 23:44
  • Thanks @NickD. I did add properties to the string, but when inserted in the org-buffer, it seems to be fontified according to whatever "higher priority" face org determines the current location/context to have. If I insert it within an org-link, for example, it'll be coloured according to org-link, and not its inherent property. Would you know of a way around that?
    – Daniel
    Jul 30, 2020 at 12:37
  • Sorry, I don't.
    – NickD
    Jul 30, 2020 at 14:10

2 Answers 2


The Elisp code below shows how it can be done.
First, some notes:

  1. You can use the display text property to replace characters visually with other text.
  2. If you want to display the bullet in red, propertize the replacement text. You can use text properties in strings for that purpose.
  3. Use the OVERRIDE flag prepend or append for the font lock keyword. This is the most sensible choice if you modify an existing mode through personal configuration. It ensures that the fontification of the original mode and your own fontification is combined and none of the two overrides the other completely. If you do not add this OVERRIDE flag and prepend your own fontification to that one of the original mode your fontification prevents tweaks by the original mode. If you append your fontification without OVERRIDE it has no effect if the original mode fontifies the same stretch of text.
  4. Add the keyword at the end of font-lock-keywords so that your fontification is not overridden by other rules (HOW argument of font-lock-add-keywords).
  5. Note, a regexp as matcher is not sufficient since it also gives false positives. For an example, matches in source blocks are also highlighted. That is the reason why I introduced the matcher function org+-match-item-marker.
(require 'org-element)
(defun org+-match-item-marker (bound)
  "Match the bullet of itemizations."
   (re-search-forward "^ *\\(-\\) " bound t)
       (goto-char (match-end 1))
       (eq (org-element-type (org-element-at-point)) 'item)))))

 '((org+-match-item-marker ;; matcher
    (1 ;; subexpression
     '(face default display #("•" 0 1 (face (:foreground "red")))) ;; face
     append ;; override
 t) ;; how
  • this is brilliant. Thanks! Why do we have to add the append flag? It seems to work without it too.
    – Daniel
    Aug 1, 2020 at 19:22
  • @dangom I added the explanation to item 3.
    – Tobias
    Aug 2, 2020 at 8:05

This is a complementary answer to the one posted by Tobias. I noticed that the following worked for me too:

(setq red-unicode-dot (propertize "•" 'font-lock-face '(:foreground "red")))
   '(("^ *\\([-]\\) "
      (0 (prog1 () (put-text-property
                    (match-beginning 1)
                    (match-end 1)
                    'display red-unicode-dot))))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.