From 6be21f1119b60334d8ac1891a5f8b744f92c7457 Mon Sep 17 00:00:00 2001 From: Protesilaos Stavrou Date: Sun, 28 Sep 2025 10:43:02 +0300 Subject: [PATCH] Redefine how themes are registered (the macro) and retrieved --- modus-operandi-deuteranopia-theme.el | 33 +++--- modus-operandi-theme.el | 29 +++-- modus-operandi-tinted-theme.el | 29 +++-- modus-operandi-tritanopia-theme.el | 33 +++--- modus-themes.el | 162 +++++++++++++-------------- modus-vivendi-deuteranopia-theme.el | 33 +++--- modus-vivendi-theme.el | 29 +++-- modus-vivendi-tinted-theme.el | 29 +++-- modus-vivendi-tritanopia-theme.el | 33 +++--- 9 files changed, 189 insertions(+), 221 deletions(-) diff --git a/modus-operandi-deuteranopia-theme.el b/modus-operandi-deuteranopia-theme.el index 2d6d412..cc9721f 100644 --- a/modus-operandi-deuteranopia-theme.el +++ b/modus-operandi-deuteranopia-theme.el @@ -34,6 +34,7 @@ +;;;###theme-autoload (eval-and-compile (unless (and (fboundp 'require-theme) load-file-name @@ -42,19 +43,6 @@ (require-theme 'modus-themes t)) (require 'modus-themes)) -;;;###theme-autoload - (deftheme modus-operandi-deuteranopia - "Deuteranopia-optimized theme with a white background. -This variant is optimized for users with red-green color -deficiency (deuteranopia). It conforms with the highest -legibility standard for color contrast between background and -foreground in any given piece of text, which corresponds to a -minimum contrast in relative luminance of 7:1 (WCAG AAA -standard)." - :background-mode 'light - :kind 'color-scheme - :family 'modus) - (defconst modus-operandi-deuteranopia-palette '( ;;; Basic values @@ -531,10 +519,19 @@ represents." :initialize #'custom-initialize-default :link '(info-link "(modus-themes) Palette overrides")) - (modus-themes-theme modus-operandi-deuteranopia - modus-operandi-deuteranopia-palette - modus-operandi-deuteranopia-palette-overrides) - - (provide-theme 'modus-operandi-deuteranopia)) + (modus-themes-theme + modus-operandi-deuteranopia + modus-themes + "Deuteranopia-optimized theme with a white background. +This variant is optimized for users with red-green color +deficiency (deuteranopia). It conforms with the highest +legibility standard for color contrast between background and +foreground in any given piece of text, which corresponds to a +minimum contrast in relative luminance of 7:1 (WCAG AAA +standard)." + light + modus-operandi-deuteranopia-palette + modus-operandi-deuteranopia-palette-user + modus-operandi-deuteranopia-palette-overrides)) ;;; modus-operandi-deuteranopia-theme.el ends here diff --git a/modus-operandi-theme.el b/modus-operandi-theme.el index 2de5901..5bcba9e 100644 --- a/modus-operandi-theme.el +++ b/modus-operandi-theme.el @@ -34,6 +34,7 @@ +;;;###theme-autoload (eval-and-compile (unless (and (fboundp 'require-theme) load-file-name @@ -42,17 +43,6 @@ (require-theme 'modus-themes t)) (require 'modus-themes)) -;;;###theme-autoload - (deftheme modus-operandi - "Elegant, highly legible theme with a white background. -Conforms with the highest legibility standard for color contrast -between background and foreground in any given piece of text, -which corresponds to a minimum contrast in relative luminance of -7:1 (WCAG AAA standard)." - :background-mode 'light - :kind 'color-scheme - :family 'modus) - (defconst modus-operandi-palette '( ;;; Basic values @@ -529,10 +519,17 @@ represents." :initialize #'custom-initialize-default :link '(info-link "(modus-themes) Palette overrides")) - (modus-themes-theme modus-operandi - modus-operandi-palette - modus-operandi-palette-overrides) - - (provide-theme 'modus-operandi)) + (modus-themes-theme + modus-operandi + modus-themes + "Elegant, highly legible theme with a white background. +Conforms with the highest legibility standard for color contrast +between background and foreground in any given piece of text, +which corresponds to a minimum contrast in relative luminance of +7:1 (WCAG AAA standard)." + light + modus-operandi-palette + modus-operandi-palette-user + modus-operandi-palette-overrides)) ;;; modus-operandi-theme.el ends here diff --git a/modus-operandi-tinted-theme.el b/modus-operandi-tinted-theme.el index 297b978..5399940 100644 --- a/modus-operandi-tinted-theme.el +++ b/modus-operandi-tinted-theme.el @@ -34,6 +34,7 @@ +;;;###theme-autoload (eval-and-compile (unless (and (fboundp 'require-theme) load-file-name @@ -42,17 +43,6 @@ (require-theme 'modus-themes t)) (require 'modus-themes)) -;;;###theme-autoload - (deftheme modus-operandi-tinted - "Elegant, highly legible theme with a light ochre background. -Conforms with the highest legibility standard for color contrast -between background and foreground in any given piece of text, -which corresponds to a minimum contrast in relative luminance of -7:1 (WCAG AAA standard)." - :background-mode 'light - :kind 'color-scheme - :family 'modus) - (defconst modus-operandi-tinted-palette '( ;;; Basic values @@ -529,10 +519,17 @@ represents." :initialize #'custom-initialize-default :link '(info-link "(modus-themes) Palette overrides")) - (modus-themes-theme modus-operandi-tinted - modus-operandi-tinted-palette - modus-operandi-tinted-palette-overrides) - - (provide-theme 'modus-operandi-tinted)) + (modus-themes-theme + modus-operandi-tinted + modus-themes + "Elegant, highly legible theme with a light ochre background. +Conforms with the highest legibility standard for color contrast +between background and foreground in any given piece of text, +which corresponds to a minimum contrast in relative luminance of +7:1 (WCAG AAA standard)." + light + modus-operandi-tinted-palette + modus-operandi-tinted-palette-user + modus-operandi-tinted-palette-overrides)) ;;; modus-operandi-tinted-theme.el ends here diff --git a/modus-operandi-tritanopia-theme.el b/modus-operandi-tritanopia-theme.el index fd256ea..a176d14 100644 --- a/modus-operandi-tritanopia-theme.el +++ b/modus-operandi-tritanopia-theme.el @@ -34,6 +34,7 @@ +;;;###theme-autoload (eval-and-compile (unless (and (fboundp 'require-theme) load-file-name @@ -42,19 +43,6 @@ (require-theme 'modus-themes t)) (require 'modus-themes)) -;;;###theme-autoload - (deftheme modus-operandi-tritanopia - "Tritanopia-optimized theme with a white background. -This variant is optimized for users with blue-yellow color -deficiency (tritanopia). It conforms with the highest -legibility standard for color contrast between background and -foreground in any given piece of text, which corresponds to a -minimum contrast in relative luminance of 7:1 (WCAG AAA -standard)." - :background-mode 'light - :kind 'color-scheme - :family 'modus) - (defconst modus-operandi-tritanopia-palette '( ;;; Basic values @@ -531,10 +519,19 @@ represents." :initialize #'custom-initialize-default :link '(info-link "(modus-themes) Palette overrides")) - (modus-themes-theme modus-operandi-tritanopia - modus-operandi-tritanopia-palette - modus-operandi-tritanopia-palette-overrides) - - (provide-theme 'modus-operandi-tritanopia)) + (modus-themes-theme + modus-operandi-tritanopia + modus-themes + "Tritanopia-optimized theme with a white background. +This variant is optimized for users with blue-yellow color +deficiency (tritanopia). It conforms with the highest +legibility standard for color contrast between background and +foreground in any given piece of text, which corresponds to a +minimum contrast in relative luminance of 7:1 (WCAG AAA +standard)." + light + modus-operandi-tritanopia-palette + modus-operandi-tritanopia-palette-user + modus-operandi-tritanopia-palette-overrides)) ;;; modus-operandi-tritanopia-theme.el ends here diff --git a/modus-themes.el b/modus-themes.el index 8d96081..190ecc3 100644 --- a/modus-themes.el +++ b/modus-themes.el @@ -995,68 +995,40 @@ C1 and C2 are color values written in hexadecimal RGB." (+ (modus-themes-wcag-formula c2) 0.05)))) (max ct (/ ct)))) -(defun modus-themes--modus-p (theme) - "Return non-nil if THEME name has a modus- prefix." - (string-prefix-p "modus-" (symbol-name theme))) +(defvar modus-themes-registered-items nil + "List of defined themes. +This list is instantiated by the `modus-themes-theme' macro. Themes +that build on top of Modus but for some reason cannot use that macro +must define theme properties to include those that the macro specifies.") (defun modus-themes--list-enabled-themes () "Return list of `custom-enabled-themes' with modus- prefix." - (seq-filter #'modus-themes--modus-p custom-enabled-themes)) + (seq-intersection modus-themes-registered-items custom-enabled-themes)) -(defun modus-themes--load-no-enable (theme) - "Load but do not enable THEME if it belongs to `custom-known-themes'." - (unless (memq theme custom-known-themes) - (load-theme theme :no-confirm :no-enable))) - -(defun modus-themes--enable-themes () - "Enable the Modus themes." - (mapc #'modus-themes--load-no-enable modus-themes-items)) - -(defun modus-themes--list-known-themes () - "Return list of `custom-known-themes' with modus- prefix." - (modus-themes--enable-themes) - (seq-filter #'modus-themes--modus-p custom-known-themes)) - -(defun modus-themes--current-theme () +(defun modus-themes-get-current-theme () "Return first enabled Modus theme." (car (modus-themes--list-enabled-themes))) -(defun modus-themes--palette-symbol (theme &optional suffix) - "Return THEME palette as a symbol of the form THEME-palette. -With optional SUFFIX, return THEME-palette-SUFFIX as a symbol." - (when theme - (intern - (if suffix - (format "%s-palette-%s" theme suffix) - (format "%s-palette" theme))))) - -(defun modus-themes--palette-value (theme &optional overrides) - "Return palette value of THEME with optional OVERRIDES." - (let* ((core-palette (symbol-value (modus-themes--palette-symbol theme))) - (user-palette (symbol-value (modus-themes--palette-symbol theme "user"))) - (base-value (append user-palette modus-themes-common-palette-user core-palette))) - (if overrides - (append (symbol-value (modus-themes--palette-symbol theme "overrides")) - modus-themes-common-palette-overrides - base-value) - base-value))) - -(defun modus-themes--current-theme-palette (&optional overrides) - "Return palette value of active Modus theme, else produce `user-error'. -With optional OVERRIDES return palette value plus whatever -overrides." - (if-let* ((theme (modus-themes--current-theme))) - (if overrides - (modus-themes--palette-value theme :overrides) - (modus-themes--palette-value theme)) - (user-error "No enabled Modus theme could be found"))) +(defun modus-themes-get-theme-palette (&optional theme) + "Return palette value of active `modus-themes-registered-items' THEME. +If THEME is nil, use the return value of `modus-themes-get-current-theme'." + (let ((theme (or theme (modus-themes-get-current-theme)))) + (unless (memq theme modus-themes-registered-items) + (error "The theme `%s' is not among `modus-themes-registered-items'" theme)) + (if-let* ((properties (get theme 'theme-properties)) + (core-palette (plist-get properties :modus-core-palette))) + (let ((user-palette (plist-get properties :modus-user-palette)) + (overrides-palette (plist-get properties :modus-overrides-palette))) + (append core-palette user-palette overrides-palette)) + (error "The theme must have at least a `:modus-core-palette' property")))) (defun modus-themes--disable-themes () "Disable themes per `modus-themes-disable-other-themes'." - (mapc #'disable-theme - (if modus-themes-disable-other-themes - custom-enabled-themes - (modus-themes--list-known-themes)))) + (mapc + #'disable-theme + (if modus-themes-disable-other-themes + custom-enabled-themes + modus-themes-registered-items))) (defun modus-themes-load-theme (theme) "Load THEME while disabling other themes. @@ -1085,11 +1057,9 @@ This function is used in the macros `modus-themes-theme', `modus-themes-with-colors'." (let ((value (car (alist-get color palette)))) (cond - ((or (stringp value) - (eq value 'unspecified)) + ((stringp value) value) - ((and (symbolp value) - value) + ((and (symbolp value) value) (modus-themes--retrieve-palette-value value palette)) (t 'unspecified)))) @@ -1114,7 +1084,7 @@ If COLOR is not present in the palette, return the `unspecified' symbol, which is safe when used as a face attribute's value." (if-let* ((palette (if theme (modus-themes--palette-value theme overrides) - (modus-themes--current-theme-palette overrides))) + (modus-themes-get-theme-palette overrides))) (value (modus-themes--retrieve-palette-value color palette))) value 'unspecified)) @@ -1143,13 +1113,8 @@ symbol, which is safe when used as a face attribute's value." (complete-with-action action candidates string pred))))) (defun modus-themes--completion-table-candidates () - "Render `modus-themes--list-known-themes' as completion with theme category." - (modus-themes--completion-table 'theme (modus-themes--list-known-themes))) - -(define-obsolete-function-alias - 'modus-themes--select-prompt - 'modus-themes-select-prompt - "4.9.0") + "Render `modus-themes-registered-items' as completion table with theme category." + (modus-themes--completion-table 'theme modus-themes-registered-items)) (defun modus-themes-select-prompt (&optional prompt themes history) "Minibuffer prompt to select a Modus theme. @@ -1310,7 +1275,7 @@ color mappings instead of the complete palette." (defun modus-themes-list-colors-current (&optional mappings) "Like `modus-themes-list-colors' with optional MAPPINGS for the current theme." (interactive "P") - (modus-themes-list-colors (modus-themes--current-theme) mappings)) + (modus-themes-list-colors (modus-themes-get-current-theme) mappings)) (defalias 'modus-themes-preview-colors-current 'modus-themes-list-colors-current "Alias for `modus-themes-list-colors-current'.") @@ -4369,27 +4334,54 @@ FG and BG are the main colors." ;;;; Instantiate a Modus theme ;;;###autoload -(defmacro modus-themes-theme (name palette &optional overrides) - "Bind NAME's color PALETTE around face specs and variables. -Face specifications are passed to `custom-theme-set-faces'. -While variables are handled by `custom-theme-set-variables'. -Those are stored in `modus-themes-faces' and -`modus-themes-custom-variables' respectively. +(defmacro modus-themes-theme (name family description background-mode core-palette user-palette overrides-palette) + "Define Modus theme with symbol NAME. +FAMILY is a symbol of the collection to which the theme belongs. +DESCRIPTION is a string describing the theme. BACKGROUND-MODE is a +symbol of either `dark' or `light', in reference to the theme's background. -Optional OVERRIDES are appended to PALETTE, overriding -corresponding entries." +CORE-PALETTE is the symbol of an alist in which each element is of the +form (SYMBOL COLOR). USER-PALETTE is for themes that build on top of +the CORE-PALETTE and, as such, can be nil. OVERRIDES-PALETTE is the +symbol of a palette like the aforementioned intended for user +customization and, as such, can be nil. + +The values of CORE-PALETTE and USER-PALETTE are used to instantiate the +theme. OVERRIDES-PALETTE is only registed as a theme property. + +Make the defined theme symbol have the following `theme-properties': + +- :kind +- :background-mode +- :family +- :modus-core-palette +- :modus-user-palette +- :modus-overrides-palette" (declare (indent 0)) (let ((sym (gensym)) - (colors (mapcar #'car (symbol-value palette)))) - `(let* ((c '((class color) (min-colors 256))) - (,sym (modus-themes--palette-value ',name ',overrides)) - ,@(mapcar (lambda (color) - (list color - `(modus-themes--retrieve-palette-value ',color ,sym))) - colors)) - (ignore c ,@colors) ; Silence unused variable warnings - (custom-theme-set-faces ',name ,@modus-themes-faces) - (custom-theme-set-variables ',name ,@modus-themes-custom-variables)))) + (colors (mapcar #'car (symbol-value core-palette))) + (theme-exists-p (custom-theme-p name))) + `(progn + (let* ((c '((class color) (min-colors 256))) + (,sym (append ,core-palette ,user-palette nil)) + ,@(mapcar (lambda (color) + (list color + `(modus-themes--retrieve-palette-value ',color ,sym))) + colors)) + ,@(unless theme-exists-p + (list `(custom-declare-theme + ',name ',family + ,description + (list :kind 'color-scheme :background-mode ',background-mode :family ',family + :modus-core-palette ',core-palette :modus-user-palette ',user-palette + :modus-overrides-palette ',overrides-palette)))) + ,@(when user-palette + (list `(add-to-list 'modus-themes-registered-items ',name))) + (ignore c ,@colors) ; Silence unused variable warnings + (custom-theme-set-faces ',name ,@modus-themes-faces) + (custom-theme-set-variables ',name ,@modus-themes-custom-variables) + ,@(unless theme-exists-p + (list `(provide-theme ',name))))))) ;;;; Use theme colors @@ -4402,9 +4394,9 @@ corresponding entries." ;; instantiate the actual theme's palette. We have to do this ;; otherwise the macro does not work properly when called from ;; inside a function. - (colors (mapcar #'car (modus-themes--current-theme-palette)))) + (colors (mapcar #'car (modus-themes-get-theme-palette)))) `(let* ((c '((class color) (min-colors 256))) - (,sym (modus-themes--current-theme-palette :overrides)) + (,sym (modus-themes-get-theme-palette :overrides)) ,@(mapcar (lambda (color) (list color `(modus-themes--retrieve-palette-value ',color ,sym))) diff --git a/modus-vivendi-deuteranopia-theme.el b/modus-vivendi-deuteranopia-theme.el index e9e617c..ff5e328 100644 --- a/modus-vivendi-deuteranopia-theme.el +++ b/modus-vivendi-deuteranopia-theme.el @@ -34,6 +34,7 @@ +;;;###theme-autoload (eval-and-compile (unless (and (fboundp 'require-theme) load-file-name @@ -42,19 +43,6 @@ (require-theme 'modus-themes t)) (require 'modus-themes)) -;;;###theme-autoload - (deftheme modus-vivendi-deuteranopia - "Deuteranopia-optimized theme with a black background. -This variant is optimized for users with red-green color -deficiency (deuteranopia). It conforms with the highest -legibility standard for color contrast between background and -foreground in any given piece of text, which corresponds to a -minimum contrast in relative luminance of 7:1 (WCAG AAA -standard)." - :background-mode 'dark - :kind 'color-scheme - :family 'modus) - (defconst modus-vivendi-deuteranopia-palette '( ;;; Basic values @@ -531,10 +519,19 @@ represents." :initialize #'custom-initialize-default :link '(info-link "(modus-themes) Palette overrides")) - (modus-themes-theme modus-vivendi-deuteranopia - modus-vivendi-deuteranopia-palette - modus-vivendi-deuteranopia-palette-overrides) - - (provide-theme 'modus-vivendi-deuteranopia)) + (modus-themes-theme + modus-vivendi-deuteranopia + modus-themes + "Deuteranopia-optimized theme with a black background. +This variant is optimized for users with red-green color +deficiency (deuteranopia). It conforms with the highest +legibility standard for color contrast between background and +foreground in any given piece of text, which corresponds to a +minimum contrast in relative luminance of 7:1 (WCAG AAA +standard)." + dark + modus-vivendi-deuteranopia-palette + modus-vivendi-deuteranopia-palette-user + modus-vivendi-deuteranopia-palette-overrides)) ;;; modus-vivendi-deuteranopia-theme.el ends here diff --git a/modus-vivendi-theme.el b/modus-vivendi-theme.el index 10b95ec..5a895e8 100644 --- a/modus-vivendi-theme.el +++ b/modus-vivendi-theme.el @@ -34,6 +34,7 @@ +;;;###theme-autoload (eval-and-compile (unless (and (fboundp 'require-theme) load-file-name @@ -42,17 +43,6 @@ (require-theme 'modus-themes t)) (require 'modus-themes)) -;;;###theme-autoload - (deftheme modus-vivendi - "Elegant, highly legible theme with a black background. -Conforms with the highest legibility standard for color contrast -between background and foreground in any given piece of text, -which corresponds to a minimum contrast in relative luminance of -7:1 (WCAG AAA standard)." - :background-mode 'dark - :kind 'color-scheme - :family 'modus) - (defconst modus-vivendi-palette '( ;;; Basic values @@ -529,10 +519,17 @@ represents." :initialize #'custom-initialize-default :link '(info-link "(modus-themes) Palette overrides")) - (modus-themes-theme modus-vivendi - modus-vivendi-palette - modus-vivendi-palette-overrides) - - (provide-theme 'modus-vivendi)) + (modus-themes-theme + modus-vivendi + modus-themes + "Elegant, highly legible theme with a black background. +Conforms with the highest legibility standard for color contrast +between background and foreground in any given piece of text, +which corresponds to a minimum contrast in relative luminance of +7:1 (WCAG AAA standard)." + dark + modus-vivendi-palette + modus-vivendi-palette-user + modus-vivendi-palette-overrides)) ;;; modus-vivendi-theme.el ends here diff --git a/modus-vivendi-tinted-theme.el b/modus-vivendi-tinted-theme.el index f5e6d1d..ad4739f 100644 --- a/modus-vivendi-tinted-theme.el +++ b/modus-vivendi-tinted-theme.el @@ -34,6 +34,7 @@ +;;;###theme-autoload (eval-and-compile (unless (and (fboundp 'require-theme) load-file-name @@ -42,17 +43,6 @@ (require-theme 'modus-themes t)) (require 'modus-themes)) -;;;###theme-autoload - (deftheme modus-vivendi-tinted - "Elegant, highly legible theme with a night sky background. -Conforms with the highest legibility standard for color contrast -between background and foreground in any given piece of text, -which corresponds to a minimum contrast in relative luminance of -7:1 (WCAG AAA standard)." - :background-mode 'dark - :kind 'color-scheme - :family 'modus) - (defconst modus-vivendi-tinted-palette '( ;;; Basic values @@ -529,10 +519,17 @@ represents." :initialize #'custom-initialize-default :link '(info-link "(modus-themes) Palette overrides")) - (modus-themes-theme modus-vivendi-tinted - modus-vivendi-tinted-palette - modus-vivendi-tinted-palette-overrides) - - (provide-theme 'modus-vivendi-tinted)) + (modus-themes-theme + modus-vivendi-tinted + modus-themes + "Elegant, highly legible theme with a night sky background. +Conforms with the highest legibility standard for color contrast +between background and foreground in any given piece of text, +which corresponds to a minimum contrast in relative luminance of +7:1 (WCAG AAA standard)." + dark + modus-vivendi-tinted-palette + modus-vivendi-tinted-palette-user + modus-vivendi-tinted-palette-overrides)) ;;; modus-vivendi-tinted-theme.el ends here diff --git a/modus-vivendi-tritanopia-theme.el b/modus-vivendi-tritanopia-theme.el index d802736..f93db8f 100644 --- a/modus-vivendi-tritanopia-theme.el +++ b/modus-vivendi-tritanopia-theme.el @@ -34,6 +34,7 @@ +;;;###theme-autoload (eval-and-compile (unless (and (fboundp 'require-theme) load-file-name @@ -42,19 +43,6 @@ (require-theme 'modus-themes t)) (require 'modus-themes)) -;;;###theme-autoload - (deftheme modus-vivendi-tritanopia - "Tritanopia-optimized theme with a black background. -This variant is optimized for users with blue-yellow color -deficiency (tritanopia). It conforms with the highest -legibility standard for color contrast between background and -foreground in any given piece of text, which corresponds to a -minimum contrast in relative luminance of 7:1 (WCAG AAA -standard)." - :background-mode 'dark - :kind 'color-scheme - :family 'modus) - (defconst modus-vivendi-tritanopia-palette '( ;;; Basic values @@ -531,10 +519,19 @@ represents." :initialize #'custom-initialize-default :link '(info-link "(modus-themes) Palette overrides")) - (modus-themes-theme modus-vivendi-tritanopia - modus-vivendi-tritanopia-palette - modus-vivendi-tritanopia-palette-overrides) - - (provide-theme 'modus-vivendi-tritanopia)) + (modus-themes-theme + modus-vivendi-tritanopia + modus-themes + "Tritanopia-optimized theme with a black background. +This variant is optimized for users with blue-yellow color +deficiency (tritanopia). It conforms with the highest +legibility standard for color contrast between background and +foreground in any given piece of text, which corresponds to a +minimum contrast in relative luminance of 7:1 (WCAG AAA +standard)." + dark + modus-vivendi-tritanopia-palette + modus-vivendi-tritanopia-palette-user + modus-vivendi-tritanopia-palette-overrides)) ;;; modus-vivendi-tritanopia-theme.el ends here