Making Org Agenda Look Beautiful

Associated Youtube Video: https://youtu.be/a_WNtuefREM

Associated Odysee Video: https://odysee.com/@LibrePhoenix:8/making-org-agenda-look-beautiful:0

This is a brief overview of how I rice my org agenda inside of Emacs.

Overview

While the default org agenda view is a nice text-based overview of your todo lists, I find it difficult to navigate and bloated, for example: 2023-12-30-making-org-agenda-look-beautiful-1.jpg

So, I’ve riced by org agenda to be more minimal and easy to navigate, like this: 2023-12-30-making-org-agenda-look-beautiful-2.jpg

So, where do we start?

Text Centering with Olivetti

The wider a block of text spans, the more difficult it is to read. To solve this, a package called Olivetti was made. Once you install it from MELPA, it can be activated easily by calling M-x olivetti-mode.

To automatically activate this upon opening org-agenda, you can define an open hook, and add it to the org-agenda-mode-hook:

;; Function to be run when org-agenda is opened
(defun org-agenda-open-hook ()
  "Hook to be run when org-agenda is opened"
  (olivetti-mode))

;; Adds hook to org agenda mode, making follow mode active in org agenda
(add-hook 'org-agenda-mode-hook 'org-agenda-open-hook)

Show Only Relevant Information

I easily get overwhelmed by too much information. Personally, I think the default view of org agenda is too cluttered. This can be fixed quite easily with a few configuration variables. Firstly, I show only one day at a time, the current day:

;; Only show one day of the agenda at a time
(setq org-agenda-span 1
      org-agenda-start-day "+0d")

You can go to the next day with the function org-agenda-later, and the previous dday with org-agenda-earlier, which I have bound to ] and [ respectively.

If you utilize #+SCHEDULED and #+DEADLINE for your todo items, along with normal timestamps, the following variables will reduce duplicate entries (in case you have an item with multiple of these designations at a time):

;; Hide duplicates of the same todo item
;; If it has more than one of timestamp, scheduled,
;; or deadline information
(setq org-agenda-skip-timestamp-if-done t
      org-agenda-skip-deadline-if-done t
      org-agenda-skip-scheduled-if-done t
      org-agenda-skip-scheduled-if-deadline-is-shown t
      org-agenda-skip-timestamp-if-deadline-is-shown t)

I also personally find the ………. and time grid all over the place to be annoying, and those I disable via:

;; Ricing org agenda
(setq org-agenda-current-time-string "")
(setq org-agenda-time-grid '((daily) () "" ""))

If you like having the time grid around, you can mess with the time values and time strings in org-agenda-time-grid instead of nuking the time grid:

;; A minimal time grid instead
(setq org-agenda-time-grid '((daily) (600 1200 1800) "---" "-----"))

If you want to go even further with some of this, configuring faces relevant to org agenda may be desired. I mess with these ones:

  • org-agenda-date
  • org-agenda-date-today
  • org-agenda-date-weekend
  • org-agenda-date-weekend-today

Category Icons

If you go through all of your agenda files and give them unique categories with the #+CATEGORY keyword, you can add icons to each todo item by setting the org-agenda-category-icon-alist. You’ll probably also want all-the-icons installed to utilize this to its full potential. This is mine:

;; Add icons!
(setq org-agenda-category-icon-alist
      `(("Teaching.p" ,(list (all-the-icons-faicon "graduation-cap" :height 0.8)) nil nil :ascent center)
        ("Family.s" ,(list (all-the-icons-faicon "home" :v-adjust 0.005)) nil nil :ascent center)
        ("Producer.p" ,(list (all-the-icons-faicon "youtube-play" :height 0.9)) nil nil :ascent center)
        ("Bard.p" ,(list (all-the-icons-faicon "music" :height 0.9)) nil nil :ascent center)
        ("Stories.s" ,(list (all-the-icons-faicon "book" :height 0.9)) nil nil :ascent center)
        ("Author.p" ,(list (all-the-icons-faicon "pencil" :height 0.9)) nil nil :ascent center)
        ("Gamedev.s" ,(list (all-the-icons-faicon "gamepad" :height 0.9)) nil nil :ascent center)
        ("Knowledge.p" ,(list (all-the-icons-faicon "database" :height 0.8)) nil nil :ascent center)
        ("Personal.p" ,(list (all-the-icons-material "person" :height 0.9)) nil nil :ascent center)
))

At this point the category icons and the category name will both appear in your agenda view, which is unecessary. To get rid of the category name, you can set org-agenda-prefix-format. There’s alot of stuff going on with this variable, since it uses escape sequences, so definitely check out that variable if you’d like to tweak it differently than me.

I set my org-agenda-prefix-format to the following:

;; Remove category names and scheduling type from agenda view
(setq org-agenda-prefix-format '(
(agenda . "  %?-2i %t ")
 (todo . " %i %-12:c")
 (tags . " %i %-12:c")
 (search . " %i %-12:c")))

This removes category names and the scheduling type (timestamp, #+SCHEDULED, #+DEADLINE).

Todo Item Grouping with Org Super Agenda

Org Super Agenda is one of my favorite Emacs packages. Essentially, it allows you to filter all the todo items in your agenda into neat and manageable groups. To set it up, start by installing org-super-agenda from MELPA, and then include the required boilerplate in your config:

;; Load org-super-agenda
(require 'org-super-agenda)
(org-super-agenda-mode t)

To set up your todo item groups, you need to set the org-super-agenda-groups variable. The project has an entire walkthrough complete with examples of what you can do with this, but essentially, here is a simple example to demonstrate how it works:

(setq org-super-agenda-groups
       '(;; Each group has an implicit boolean OR operator between its selectors.

         ;; This is the first filter, anything found here
         ;; will be placed in this group
         ;; even if it matches following groups
         (:name " Overdue" ; Name
                :scheduled past ; Filter criteria
                :order 2 ; Order it should appear in agenda view
                :face 'error) ; Font face used for text

         ;; This is the second filter, anything not found
         ;; from the first filter, but found here,
         ;; will be placed in this group
         ;; even if it matches following groups
         (:name "Personal" ; Name
                :file-path "Personal" ; Filter criteria
                :order 3 ; Order it should appear in the agenda view
                :face 'error) ; Font faced used for text

         ;; Third filter..
         (:name "Work"  ; Name
                :file-path "Work" ; Filter criteria
                :order 3 ; Order it should appear in the agenda view
                :face 'error) ; Font face used for text

         ;; Fourth filter..
         (:name " Today "  ; Optionally specify section name
                :time-grid t ; Use the time grid
                :date today ; Filter criteria
                :scheduled today ; Another filter criteria
                :order 1 ; Order it should appear in the agenda view
                :face 'warning) ; Font face used for text
        )
)

As you can read in the example and comments, the order in which you declare the groups matters, since once todo items are collected into the groups on a first-come first-serve basis, where the beginning filters have most precedence.

For a slightly more complex example, this is what my org-super-agenda-groups config looks like:

;; From the official librephoenix config
(setq org-super-agenda-groups
       '(;; Each group has an implicit boolean OR operator between its selectors.
         (:name " Overdue "  ; Optionally specify section name
                :scheduled past
                :order 2
                :face 'error)

         (:name "Personal "
                :and(:file-path "Personal.p" :not (:tag "event"))
                :order 3)

         (:name "Family "
                :and(:file-path "Family.s" :not (:tag "event"))
                :order 3)

         (:name "Teaching "
                :and(:file-path "Teaching.p" :not (:tag "event"))
                :order 3)

         (:name "Gamedev "
                :and(:file-path "Gamedev.s" :not (:tag "event"))
                :order 3)

         (:name "Youtube "
                :and(:file-path "Producer.p" :not (:tag "event"))
                :order 3)

         (:name "Music "
                :and(:file-path "Bard.p" :not (:tag "event"))
                :order 3)

         (:name "Storywriting "
                :and(:file-path "Stories.s" :not (:tag "event"))
                :order 3)

         (:name "Writing "
                :and(:file-path "Author.p" :not (:tag "event"))
                :order 3)

         (:name "Learning "
                :and(:file-path "Knowledge.p" :not (:tag "event"))
                :order 3)

          (:name " Today "  ; Optionally specify section name
                :time-grid t
                :date today
                :scheduled today
                :order 1
                :face 'warning)

Modernizing it Just a Little Bit More

There’s no denying that Emacs doesn’t look modern, but one remedy to that is called Org Modern! This affects both org-mode and org-agenda, and I think it adds a nice touch by making various things (such as todo keywords) visually pop out with a button-like style.

The setup is really easy, just install org-modern from MELPA, and call global-org-modern-mode in your config. I’ve noticed it sometimes misbehaves if it’s called before org-mode loads, so I activate it like so:

;; Load org-modern
(with-eval-after-load 'org (global-org-modern-mode))

So There You Have It!

I hope you found this useful! Happy ricing :)

Donation Links

If you have found my work to be helpful, please consider donating via one of the following links. Thank you very much!