How to make all-day events stand out in org-mode

Last week, I managed to colorize all-day events on my agenda in a unique color that stands out from the rest. A bit of a hack and a workaround, this is a highly specific solution to a challenge I’ve been trying to solve for about a year. In this post, I will explain why I need this, and how I did it.

This picture will show you what the result looks like on my agenda:

A part of my agenda showing the main colors in use and their respective header type. Notice the color key in the brown square

All-day events such as holidays or vacations show in bold yellow (color 3). Scheduled events show in green (color 2). Appointment and meetings are in sky blue (color 1).

Background

My use-case is important to understand the “why” here. There are three time-related categories I use in org-mode for different purposes.

A couple of weeks ago, I stumbled upon an option to add Emacs' holidays into org-agenda using %%(org-calendar-holiday). By default, this shows all holidays Emacs has included, which is quite a few. For me, this is good, as I want to be aware of additional events and holidays relate to other religions which affect people I work with.

Conditions

Now that I reminded you how I work with org-mode and why I wanted a third color on my agenda, we can dive into the how.

Here are the conditions I have to make this happen:

  1. Integrating Emacs' diary with diary-block entries
  2. Modifying the theme to customize headers created by Emacs' diary
  3. Nesting the diary-blocks under timeless headers (tags are OK)
  4. Changing the default in agenda, so events without a specific time of day (hour) show at the top (org-mode’s default is to show them at the bottom)

Emacs' diary was introduced before org-mode, and org-mode can integrate certain diary features. Here, we will be dealing with special diary entries that can be implemented directly into org files as-is.

A quick look through the manual entry above will show you what a diary-block is, and its timestamp format (which is different than org-mode format). Fortunately for us org-mode users, entering a special diary entry also works from org-mode’s agenda. Just hover over the day you want and use the sexp entries as explained in the manual above: i a for an anniversary (like a birthday, repeats every year), i b for block (several days, as in a case of a vacation) and i-c for cyclic event’s, Diary’s way to repeat a certain even every X number of days. That last is not as useful for me, because all events that cycle this way that I can think of fall within the timed event category for me, as explained in the first part.

So, in a way, creating all-day events in org-mode has its own built-in “capture template”.

Consider the “Demo for post” in the image above which I created for this demonstration. To create it, I hovered over Saturday. I hit i b to create a block. A diary window popped up and automatically create the diary timestamp range for that one day: %%(diary-block 6 12 2021 6 12 2021). I then jumped to the beginning of the line (C-a), activated the marker (C-spc), jumped to the end of the line (C-e), and killed (C-k) the date. I then opened my weekly.org file, which contains weekly events, and under a header I made for that specific test event (we will get to that in a minute), I planted that line.

Even better: You can activate your marker on the agenda and start highlighting days from the first day you want to the last. Creating a diary-block event at this point with i b will create a range of these days automatically for you.

With a bit of tweaking, I can probably create a simple macro to capture the different strokes and make this whole process automatic. For now I do it manually.

The next part of the process requires that we customize our Emacs' theme to get the color we want for all-day events. In the image above, this is color 3.

Customizing a theme in Emacs is not too hard if you introduce little changes, like tweaking with a specific color. You can read how I did this before. In our case, the diary events in the agenda are marked differently than their neighboring events created by org-mode. You can see this when you place the mark over a certain heading and call C-x customize-face (no need to change anything yet, just notice how Emacs calls the header in the parenthesis).

Diary events are called “org-agenda-calendar-sexp”. This means that in the theme, we can add a line that defines this face and gives it a certain color. In my theme, I’ve added this line:

(org-agenda-calendar-sexp :foreground yellow :weight bold)

My theme defines the color “yellow” and the weight “bold” elsewhere at the beginning of the theme. Yours probably does something similar. Just look to see how the other org-mode related theme adjustments are named and follow suit.

Next, we want to be able to work with these special events. Since they are not created in org-mode, they do not have a header by default, so we can’t do anything with them like adding tags, link back to them in the journal, or organize and collapse them. To fix this, I created a header for each one of the diary events. For example, say I create a header called “Alex” in my routine.org file. Under the header, I will nest two %%(diary-anniversary) events: One for Alex’s birthday, the other for our anniversary. Similarly, on the weekly.org file (my weekly planner), I can create a header “Visiting parents @ NJ” for the three days I’m planning to go with Alex. Under that header, I’ll place a %%(diary-block) with the appropriate date range.

These headers shouldn’t have a timestamp, otherwise they’ll interfere with the information the agenda displays from the diary entries. However, one thing I will do is to tag the header with “Alex” as I usually do with people I know on a regular basis. Later on, when I’m in NJ on the vacation, I will link to that header (because I can’t link to the diary entry) from my journal.org and reflect on my experiences2 as they happen.

Finally, I noted that my agenda shows hourless events first. This helps with the general order of things, as it will show deadlines and tasks I haven’t scheduled first, so I can take care of them. This is achieved by modifying org-sort-agenda-notime-is-late to nil:

(setq org-sort-agenda-notime-is-late nil)

Comments

As I noted, this system is highly specific and will probably not work for you as-is. There are a few issues here that I’m aware of and I want to fix:

  1. As easy as it is to enter diary events in org-mode, these are not capture templates. This creates an interruption in the forc… um, flow.

  2. The diary itself (which you need to create) can break at times and not understand what date you’re highlighting. It will display a message saying something like “I don’t know what date you mean.” The only way to fix this that I found so far is to exit Emacs. There has to be a reason for this and a better solution.

  3. While this works, this is a somewhat ugly hack, which requires modifying my theme and incorporate diary parts which I usually don’t use. I might find a mode elegant elisp solution.

Footnotes


  1. I often reflect on events that happened the previous day when I journal and add them in retro-respect. Besides giving me a clear idea of what I did that day and where I went to, I can also write a journal entry that is connected to that event to reflect on what happened, with who, where. Org-mode is very easy to work throughout time: I can enter an event that happened three weeks ago just as easily as I can enter a new one today. ↩︎

  2. This is a good place to point out that I stopped using org-mode’s default links, which point to a header in an org file. These are too fragile: as soon as you refile a header or change the title, it breaks. A way around that is to use unique IDs (briefly mentioned here, but there’s more to it), which goes beyond what I’m covering in this post. This way my journal (in the above case) will always point to that specific header, even after it moved out of weekly.org and into its respective yearly week number or file, which is how I store my org files long-term. ↩︎


Comments