CSS for Roam (3rd lesson): Advanced selectors for #tags and [[pages]]

The irresistible power of Roam, image based on unDraw.co .

Welcome to the third article on the CSS in Roam. Previously, we have covered:

This time, I would like to expand on the power of #tags and [[pages]] and show how they can affect other elements. Before, however, we must prepare ourselves: First, a little recapitulation of basic simple selectors in CSS, then we will learn about the so-called combinators that allow us to create complex selectors, finally, we will look at the code of Roam and what it allows us to do. With this knowledge, we will create our custom highlighting colors in Roam.

A little recapitulation — Simple selectors

We have already seen that we can select an element by its name, by its class, and by its attribute. I have also mentioned that we can select an element based on its ID even though we haven’t seen any example yet. We can visualize these options as follow:

Possible selectors (simplified), made in https://www.bottlecaps.de/rr/ui
Possible selectors (simplified), made in https://www.bottlecaps.de/rr/ui
Simple selectors (simplified), created in Railroad Diagram Generator

It is also perfectly fine to combine these and create so-called compound selectors. You can find the whole overview on selectors on W3schools web page.

Roam, like any web site, consists of HTML tags. In the previous lesson, we have already used h1 — the tag for the largest heading. If we use the tag selector like h1 { … } it will select all occurrences of large headings. This makes tag selectors often not suitable for CSS. Much more common is therefore the use of classes. Basically, almost all the important elements in Roam have one or more. We used them when we customized the appearance of the page title: Instead of using h1 that selected all large headings in the outline, we used the class .rm-title-display as the selector. Thanks to this, only the page title changed its color. Finally, last time we used the attribute selector when we changed the color of the #tag: [data-tag="something"] .

However, until now we haven't seen any ID. The most important property of IDs is that they are unique. There cannot be two elements on the web page with the same ID (otherwise it would be no ID, of course). This also shows the main difference between the class and ID. A class can be shared among elements. Yes, technically, nobody prevents you to hack it and have a unique class for every single element — but why would you do that? One element may also contain multiple classes, each of them can add a small piece of formatting. On the other hand, an element may contain.

In the code of Roam, we find ID rather rarely, but they are present in one crucial position: each and every block has its unique ID. Let’s look at the following example from the page on Roam-tricks — the public Roam database:

On the image, you can see that the block <div> containing {{POMO}} has quite a long ID. How could we use it? E.g., if we would like to change the appearance only of this one block and of nothing more. Perhaps, you have one, very special block you want to give a special love.

Example of simple selectors for Roam
Example of simple selectors for Roam
Example of simple selectors for Roam, created in Railroad Diagram Generator

You can combine these simple selectors into the already mentioned compound selectors, e.g., h1.rm-title-display. However, you don’t have to care about this too much now. We will see it in future as well as an advanced usage of attribute selectors.

Complex selectors (selection by position)

Before I can show you how #tags and [[pages]] can affect other elements, we have to learn a little bit about so-called complex selectors that allow us to select elements by their position. Do you remember that HTML is structured like a tree? It has one root and different branches. It looks like this:

Document Object Model of an HTML document. By Birger Eriksson — Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=18034500

With CSS, we can go deeper or down in the tree, and select either children or following siblings of another element. We can do that using combinators (they combine various selectors). The most basic combinator is just simply a blank space! The blank space means that the selector on the right side is a descendant of the element on the left side.

What does it mean? Imagine you want to select headlines in the right sidebar. If you write only h1 as a selector, you will match all headlines in the whole web page — which is not the thing you want. The solution is to select firstly the right sidebar using its ID #right-sidebar and then specify that you want to select its descendants h1. Writing #right-sidebar h1 will affect all h1 headings in the right sidebar (but not in the main window), even if h1 is deeply nested.

Other useful combinators are: > that selects the children (but not deeper descendants), + that selects the immediately following sibling, ~ that selects all siblings. It can be visualized like this:

Complex selectors in CSS — combinators
Complex selectors in CSS — combinators
Complex selectors in CSS

Of course, it is possible to continue and join any number of selectors in this way. Complex selectors are often necessary. E.g., we may want to select an element that has neither class nor ID. However, its parent may have one. In such a case, we can select its parent and use the appropriate combinator like a blank space or > . We will see an example soon.

Now the last step of our preparation. What can we select in Roam by position?

Examining the potential

Because we are speaking about #tags and [[pages]], we will deal with the code of the main window and the outline. Look at this example:

Structure of Roam
Structure of Roam
Structure of Roam

Have you noticed that? Basically, Roam consists of many nested <div> elements in a specific way that could be visualized like this:

Simplified structure of outlines in Roam, .roam-block contains the text of the outline. Made in Ffau Editor.

The content of our block is in the div with the class roam-block. Unfortunately, its children are not what they seem to be! In fact, from the strict perspective of the code, they are not its children! :O They are on the bottom of the image, in the two div elements with the class roam-block-container . This is not intuitive. It also means that we can use #tag neither to change the appearance of the block (because it is its parent), nor of the nested blocks (because they are nested in the different layer and not directly under our block.)

The good news is that within the block itself, we can do a lot of fun stuff because the #tags and [[pages]] are on the same depth as other elements in the block, like highlight, bold, italics, and strikethrough. The only exception to this is the unformatted text. Look at the code:

Structure of Roam
Structure of Roam
Structure of Roam — #tag and highlight on the same level

Here the first <span> contains the #tag and the following one the highlighted text.

This means that #tags and [[pages]] can influence the appearance of almost anything that follows them, either immediately (using + combinator) or anywhere in the block (using ~ combinator). We have to be just careful and put our #tags and [[pages]] in front of the elements we want to customize using CSS.

We can abstractly visualize the resulting complex selector as follows:

Complex selector for the formatted text

In practical terms of the Roam code, this would be our code for the #something followed by the formatted text (highlight, bold, italics, strikethrough):

Example of the complex selector for formatted text

Note that only the highlight uses a specific class (therefore . ), the others are mapped to standard HTML tags:

  • bold — <strong>

Now we are finally prepared for our great example.

Use #tags for custom highlighting colors

Currently (October 2020), there is no choice of color for highlighting in Roam. In the default theme, it is simply yellow. We can change it very easily for all highlights by writing into a code block in roam/css:

However, we want to have a choice from many different colors. First, let’s create two #tags, #c:blue and #c:green, on two separate blocks and then add the highlighted text after each of them.

We are almost there! Now we have to make a small change in our code block. Do you remember how to select the #tag based on its data-tag attribute? It is simple: [data-tag="NAME_OF_THE_TAG"] . You can start, e.g., with #c:blue. Note that the # is not part of the name in the data-tag attribute. Then we have to just combine with the its adjacent .roam-highlight element using +. Finally, write a simple declaration and change the background color to light blue.

Wow, that was easy! But there is one slightly annoying thing. We see our #tags! But it is very simple to get rid of them. Write appropriate selectors for you tags into into the code-block and add a simple declaration: display:none .

Do the same thing for #c:green:

Now we can make two simple optimisations to make our code a little bit easier to read and more efficient: First, we can actually select multiple different things on one line, just by putting , between different selectors. This is handy for hiding multiple different #tags:

Secondly , we can make it even better by using for our advantage that both #tags begin with “c:”. Attribute selectors in CSS do not have just = comparison but also a couple of others. We will now use ^= that matches the beginning of the string:

That is all. You can download my complete code with multiple colors, supporting highlight, italics and bold from my Gist.

In the next article, we will look one more time at the power of [[pages]] and #tags — how they can be used in combination with tables, kanban and other elements. After that, we will move to layouts — what we can do with them in Roam using CSS! So stay tuned :).

If you like my work, follow me on Twitter, check the public Roam database Roam-tricks, my personal public collection of my hacks, and yes, you can support me on Patreon or through PayPal. I would really appreciate it and you would help to make future articles happen. And remember: If it looks like a duck, if it writes like a duck, it’s probably me. [[QUACK]]!

Just a humble Roaman duck.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store