Programmatic emacs theme pallate access
#+title: technicolor #+subtitle: (almost) universal programmatic color palette access
See this accompanying [[https://aatmunbaxi.netlify.app/comp/dyn_custom_palette_access_technicolor/][blog post]] detailing the problem, solution, and example usage.
But you also use =modus-themes= sometimes, and you want to use the modus foreground color. No problem, modus offers an API too: =(modus-themes-get-color-value 'fg-main)=. Oops, the symbol of the color is different, and the function to access the color is different. I guess you can add some branches in your elisp that determine if the current theme is a DOOM or modus theme...
..except you also sometimes use =catppuccin-theme=, and =ef-themes=, and...
What to do? Add branches /ad nauseam/ to cover every possible theme you might have active?
technicolor hopes to solve this issue by letting users define a "universal color palette" in which they can access these colors from any currently-set theme coming from a list of themes the user specifies with a /single API/.
Configured properly, the above situation would reduce to =(technicolor-get-color 'foreground)=.
These are good starting points, but the user should still peer into the color palettes and decide how they want to map colors. Here is an example:
Let's say you want to access the foreground color in elisp for the DOOM themes and modus themes. You can set this in the =technicolor-colors= list: #+begin_src emacs-lisp (setq technicolor-colors '(foreground)) #+end_src The theme's above have the following accessors we can use:
The DOOM themes use =fg= for the foreground color, while Prot uses =fg-main=, so we map our "universal" palette name =foreground= to these symbols in their configurations. #+begin_src emacs-lisp (setq technicolor-themes '(("^doom-." doom-color '((foreground . fg))) ("^modus-." modus-themes-get-color-value '((foreground . fg-main))) ...)) #+end_src You can now access the current foreground color with =(technicolor-get-color 'foreground)= provided your theme matches one of the above. Note we have reproduced a subset of the default config provided.
That configuration will match all doom themes and all modus themes; you can set per-theme rules on color mappings from the universal palette by refining the regex: #+begin_src emacs-lisp ' ... ("^modus-operandi" modus-themes-get-color-value '((mappings . here))) (("^modus-vivendi" modus-themes-get-color-value '((different . mappings))) ...) #+end_src Extend the value of =technicolor-colors= to any colors that you'd like to use. If you give appropriate mappings for each group of themes, technicolor will find them.
Note that if /all/ of your themes provide the same symbol for a certain color, as is common for generic colors like "red", "blue", etc, you don't need to specify the mapping from the universal palette (but they should still be in =technicolor-colors=).
** Theme accessors technicolor depends on a theme's /accessor/ to retrieve colors. An accessor for a color should take in a color symbol and return the hexadecimal color that the theme defines for that color, or =nil= or =unspecified= if no such color exists in the palette. DOOM themes and Prot's themes ship with their own accessors.
If the theme you want to include does include one, you will have to write your own. With varying degrees of munging, this is possible (e.g. the catppuccin color accessor defined in =technicolor.el=).
or for DOOM emacs users in your =packages.el=: #+begin_src emacs-lisp (package! technicolor :recipe (:host github :repo "aatmunbaxi/technicolor")) #+end_src
If your package manager does not support git recipes and you use =use-package=, you can clone the repo and place the following in your configuration: #+begin_src emacs-lisp (use-package technicolor :load-path "path/to/cloned/technicolor") #+end_src along with any =use-package= configuration.