在 Emacs 随机切换主题

Emacs 里我目前最喜欢的主题是 modus-themesef-themes,它们都是出自 protesilaos

后续可能还会加入 doric-themes,也是 Prot 的写的主题,这个主题的颜色比较单一,写作的时候用感觉不错。

modus-themes 相对来说更成熟,覆盖了大部分的包和使用场景; ef-themes 则提供了更多的色彩选择。

一个主题用久了就会觉得有些厌倦,想有一些新鲜感,就总是会找新主题,换新主题。

平时主要用 consult-themeconsult 提供的一个方法)去切换主题,前阵子看了 emacs-更改配色方案,受到文章的启发,我找 LLM 帮我写了一些用于定时切换主题的方法。

主题切换相关 Elisp
(defvar spike-leung/candidate-themes
  '(modus-operandi
    modus-operandi-tinted
    modus-vivendi
    modus-vivendi-tinted
    ef-arbutus
    ef-autumn
    ef-bio
    ef-cherie
    ef-cyprus
    ef-dark
    ef-deuteranopia-dark
    ef-deuteranopia-light
    ef-dream
    ef-duo-dark
    ef-duo-light
    ef-eagle
    ef-elea-dark
    ef-elea-light
    ef-frost
    ef-kassio
    ef-light
    ef-maris-dark
    ef-maris-light
    ;; ef-melissa-dark
    ef-melissa-light
    ef-night
    ef-owl
    ef-reverie
    ef-rosa
    ef-spring
    ef-summer
    ef-symbiosis
    ef-trio-dark
    ef-trio-light
    ef-tritanopia-dark
    ef-tritanopia-light
    ef-winter)
  "A list of themes to randomly cycle through.")

;;; theme related
;; @see: https://emacsredux.com/blog/2025/02/03/clean-unloading-of-emacs-themes/
(defun spike-leung/disable-all-active-themes ()
  "Disable all currently active themes."
  (interactive)
  (dolist (theme custom-enabled-themes)
    (disable-theme theme)))

(defun spike-leung/apply-random-theme ()
  "Disable current themes and apply a random theme from `spike-leung/candidate-themes`."
  (interactive)
  (when (boundp 'spike-leung/candidate-themes)
    (spike-leung/disable-all-active-themes)
    (let ((theme (nth (random (length spike-leung/candidate-themes)) spike-leung/candidate-themes)))
      (condition-case err
          (progn
            ;; Load theme to ensure its specific settings/customizations are applied
            (load-theme theme :no-confirm)
            ;; Enable the theme (this actually applies it and adds to custom-enabled-themes)
            (enable-theme theme)
            (message "Applied random theme: %s" theme))
        (error (message "Error applying theme %s: %s" theme err))))))

(run-with-timer (* 15 60) (* 15 60) 'spike-leung/apply-random-theme)

Source

这段代码主要做了这几个事:

用了一阵子,它很好地满足了我的新鲜感,如果你感兴趣,不妨试试~

へ(゜∇、°)へ へ(゜∇、°)へ

说起来奇怪,如果是我自己主动切换的主题,可能切换后我马上就想换了。

但是随机切换的主题,我反而有种顺其自然的心境,更愿意先用用看。


更新

现在主要使用 modus-themes ,通过 lightdark 两个函数切换明暗主题。

代码
(use-package modus-themes
  :ensure
  :init
  (defun spike-leung/modus-themes-load-based-on-time ()
    "根据当前时间加载主题:08:00-18:00 使用浅色主题,其他时间使用深色主题。"
    (let ((hour (string-to-number (format-time-string "%H"))))
      (if (and (>= hour 8) (< hour 18))
          (modus-themes-load-random 'light)
        (modus-themes-load-random 'dark))))
  ;; 因为 desktop 会保存 face 相关内容,可能会和主题冲突,因此主题加载需要在恢复 desktop 之后
  :hook (desktop-after-read . spike-leung/modus-themes-load-based-on-time)
  :config
  (setq modus-themes-italic-constructs t
        modus-themes-bold-constructs t
        modus-themes-disable-other-themes t)
  ;; remove fill-column-indicator background
  (modus-themes-with-colors
    (custom-set-faces
     `(fill-column-indicator ((,c :background unspecified :foreground ,bg-dim :height 1.0)))))
  (defun light()
    "Toggle light theme."
    (interactive)
    (modus-themes-load-random 'light))
  (defun dark()
    "Toggle dark theme."
    (interactive)
    (modus-themes-load-random 'dark)))

Source


更新

看到 Prot 一直在更新 doric-themesef-themes ,于是又心痒痒。

ef-themes 颜色最鲜艳, modus-themes 次之, doric-themes 最单调,周三前觉得周末很远,比较煎熬,所以周一到周三使用 ef-themes ,让色彩丰富明亮一些;周四周五快到周末了,心情渐佳,改用 modus-themes ;周末应该好好休息,就用单调一些的 doric-themes ,避免过分沉浸在 Emacs 中。白天用亮色主题,晚上用暗色主题。

代码
;; theme
(use-package modus-themes
  :config
  (setq modus-themes-italic-constructs t
        modus-themes-bold-constructs t
        modus-themes-disable-other-themes t)
  ;; remove fill-column-indicator background
  (modus-themes-with-colors
    (custom-set-faces
     `(fill-column-indicator ((,c :background unspecified :foreground ,bg-dim :height 1.0))))))

(use-package ef-themes)
(use-package doric-themes)

(defun spike-leung/load-theme-by-time (light-fn dark-fn)
  "Call LIGHT-FN  to load light themes from 8:00a.m to 6:00p.m.
otherwise, call DARK-FN to load dark themes."
  (let ((hour (string-to-number (format-time-string "%H"))))
    (funcall (if (and (>= hour 8) (< hour 18)) light-fn dark-fn))))

(defun spike-leung/themes-load-random (&optional background-mode)
  "Random load themes.

Use `ef-themes' for Monday,Tuesday,Wednesday.
Use `modus-themes' on Thursday, Friday.
Use `doric-themes' on Saturday, Sunday.
Use light themes at day, use dark themes at night.

With optional BACKGROUND-MODE as a prefix argument, prompt to limit the
set of themes to either dark or light variants."
  (require 'modus-themes)
  (require 'ef-themes)
  (require 'doric-themes)
  (let ((week (string-to-number (format-time-string "%u"))))
    (cond
     ((<= week 3)
      (cond
       ((eq background-mode 'light) (ef-themes-load-random-light))
       ((eq background-mode 'dark) (ef-themes-load-random-dark))
       (t (spike-leung/load-theme-by-time #'ef-themes-load-random-light #'ef-themes-load-random-dark))))
     ((<= week 5)
      (if background-mode
          (modus-themes-load-random background-mode)
        (spike-leung/load-theme-by-time
         (lambda () (modus-themes-load-random 'light))
         (lambda () (modus-themes-load-random 'dark)))))
     (t
      (if background-mode
          (doric-themes-load-random background-mode)
        (spike-leung/load-theme-by-time
         (lambda () (doric-themes-load-random 'light))
         (lambda () (doric-themes-load-random 'dark))))))))

(defun light()
  "Load random light themes."
  (interactive)
  (spike-leung/themes-load-random 'light))

(defun dark()
  "Load random dark themes."
  (interactive)
  (spike-leung/themes-load-random 'dark))

;; 要在 desktop 加载后再执行,避免被 desktop 记录的主题覆盖,导致混乱
(add-hook 'desktop-after-read-hook #'spike-leung/themes-load-random)

Source

另外,我平时写博客会用到 Olivettief-themesdoric-themes 没有设置 fringe 的颜色,导致 Olivetti 主体和 fringe 无法区分。所以我写了个函数,在切换主题之后,获取一个和 Olivetti 主体接近的颜色作为 olivetti-fringe 的颜色。

代码
;; olivetti 是一款打字机的名字,这个模式会模仿打字机的纸张大小,写作时用
(use-package olivetti
  :diminish
  :custom
  (olivetti-style 'fancy)
  (olivetti-body-width 100)
  (olivetti-margin-width 5)
  :hook (olivetti-mode-on . spike-leung/set-olivetti-fringe-face)
  :config
  (defun spike-leung/set-olivetti-fringe-face (&rest _)
    "Set `olivetti-fringe' to `fringe''s background, fallback to `tab-bar''s background."
    (let ((bg-default (face-attribute 'default :background nil))
          (bg-fringe (face-attribute 'fringe :background nil))
          (bg-tab-bar (face-attribute 'tab-bar :background nil)))
      (if (eq bg-default bg-fringe)
          (set-face-attribute 'olivetti-fringe nil :background bg-tab-bar)
        (set-face-attribute 'olivetti-fringe nil :background bg-fringe))))

  (advice-remove 'spike-leung/themes-load-random #'spike-leung/set-olivetti-fringe-face)
  (advice-add 'spike-leung/themes-load-random :after #'spike-leung/set-olivetti-fringe-face))

其他参考

Webmentions (加载中...)

如果你想回应这篇文章,可以在你的文章或社交媒体帖子中链接这篇文章,然后提交你的 URL,你的回应随后会显示在此页面上。 (关于 Webmention)


作 者: Spike Leung

创建于: 2025-05-21 Wed 21:40

修改于: 2026-03-10 Tue 14:15

许可证: 署名—非商业性使用—相同方式共享 4.0

支持我: 用你喜欢的方式