11

I would like to use set-face-attribute to pick one of two colors for the foreground depending on whether the background of the current theme is light or dark.

The expected behavior is that the foreground changes automatically if I switch from a light background theme to a dark background theme or vice-versa.

Here is a use case:

I customize the face of stripe-hl-line as follows:

 (set-face-attribute 'stripe-hl-line nil
                        :overline   "gray"
                        :underline  "gray"
                        :foreground "dodger blue")

But I would like to change the foreground based on just if the theme is light or dark. That way I don't have to worry about customizing that face in each theme.

Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
  • That's something normally done in the theme definition. – wasamasa Feb 26 '15 at 14:24
  • Although it could get complicated, it seems like you could make use of the `color` library here. You could probably get the rgb or hex value of the background face, determine some threshold value for "dark", and set the foreground accordingly. – Dan Feb 26 '15 at 14:27
  • @wasamasa Yes. But for some faces, I don't care about their color being specific to the theme. I have updated my question with an example face I want to customize. – Kaushal Modi Feb 26 '15 at 14:28
  • @Dan What would be an appropriate hook to reevaluate the background hex? – Kaushal Modi Feb 26 '15 at 14:29
  • this is the logic you can use ` (defface changing-face '((((background dark)) :foreground "White") (t :foreground "Black")) :group 'x-mode-faces)`... Since if you want to use existing face, then you can add the middle 2 lines to the existing defface form. – Madhavan Feb 26 '15 at 14:32
  • @MadhavanKumar Seeing `(background dark)` in various `defface` declarations is what made me think that there has to be an automated way to detect the background darkness. But I don't see anything like that in `set-face-attribute` definition. I don't want to tweak the `defface`s in the packages themselves as I would like to update them from the package manager. I tried overriding deffaces in my init but that doesn't work. – Kaushal Modi Feb 26 '15 at 14:43

1 Answers1

10

Thanks to @MadhavanKumar, I focused on deriving a solution based on defface.

I realized that I can't override an existing defface but I can always create my own background color sensitive defface. Then the only question was how to use my custom defface instead of the original.

I'll use the same example of overriding the stripe-hl-line face to walk through the solution. This face is defined in the package stripe-buffer.

Here are the steps:

Step 1. Define your own defface

(defface my/stripe-hl-line
  '((((background dark))  (:overline "gray" :underline "gray" :foreground "dodger blue"))
    (t                    (:overline "gray" :underline "gray" :foreground "red")))
  "Bold face for highlighting the current line in Hl-Line mode."
  :group 'stripe-buffer)

This will result in the foreground color of my/stripe-hl-line face being dodger blue for dark backgrounds and red for light backgrounds.

Step 2. Create a function to remap the face

The face-remap-add-relative function is used to remap an existing face to a new face. In this case, I am remapping the stripe-hl-line face to my/stripe-hl-line.

(defun my/stripe-hl-line-face-remap ()
  (face-remap-add-relative 'stripe-hl-line 'my/stripe-hl-line))

Step 3. Do the remapping at appropriate location

From the stripe-buffer source, I see that the hl-line face is remapped to stripe-hl-line face. So we need to do our remapping after that remapping happens (which is in the stripe-listify-buffer function definition).

So we do our remapping by advising stripe-listify-buffer using the :after advice combinator.

(advice-add 'stripe-listify-buffer :after #'my/stripe-hl-line-face-remap)

Done!

That said, it would have been awesome to directly use something like set-face-attribute to set faces based on the background darkness.

Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179