{{TOC}}

Why would you care about CSS?

CSS stands for Cascading Style Sheets. It is a text format that allows you to describe the visual appearance of HTML documents. In DEVONthink, CSS is used to determine the visual appearance of Markdown documents when they are rendered as HTML. Whenever you select the preview mode to look at a Markdown document, you'll see it rendered as HTML in some kind of "style". Unless you do anything about it, this will be the style that is built into DEVONthink (and the HTML engine it uses). If you're happy with that, you can stop reading here.

How can you tell DEVONthink to use your own CSS?

As of the time of this writing (DEVONthink Pro Version 3.8 and DEVONthink To Go 3.5.1), there are four methods to specify your own CSS in DEVONthink:

  1. In the global preferences. In DEVONthink, you'd provide the x-devonthink-item URL to a record stored somewhere in your database(s) and containing your CSS. In DEVONthink To Go, you do the same in the settings for Markdown documents. A globally defined CSS will influence all Markdown documents in DEVONthink.

  2. Add css: URL as the first line of your Markdown document(s). The URL can be any valid URL as well as an x-devonthink-item://... one or an absolute address of a record containing your CSS definitions like /Inbox/myCss.txt.

  3. Add a HTML script element somewhere in your Markdown document, i.e. a piece of code looking like that <style> Style definitions go here</style> If you go for this option, make sure that you do not have any empty lines in the style element. Those will break everything.

  4. Add a HTML link element to the top of your Markdown document like so <link rel="steylsheet" href="URL"> Again, the URL parameter can be anything from a usual URL to a x-devonthink-item://... one or an absolute location like /Inbox/myCSS.txt.

The last three methods will add CSS only to those Markdown documents where you insert them. So they're not really useful if you want to make sure that all your Markdown documents look the same.

What does CSS look like?

A style sheet is a sequence of definitions of the form

selector: {
  attribute1: value1;
  attribute2: value2;
  ...;
}

where selector specifies the HTML elements to which the following definitions are applied. Note that there must be a colon between the attribute and its value, and that there must be a semicolon after the value.

All that selector and attribute stuff might sound overly complicated, so let's consider a simple example:

body {
  color: black;
  background-color: white;
}

The baseline: white background, black text

These lines ensure that the body element of an HTML document is displayed black (color) on white (background-color). As styles are "cascading" (the C in CSS), this simple definition ensures that all text in your rendered Markdown appears black on white – all elements are children of the body element and by default inherit its style definitions.

So what would you do if you wanted to make quotes appear indented and with a different background color? You'd have to define these attributes for the element blockquote:

blockquote {
  background-color: lightgrey;
  margin-left: 2em;
}

The background-color should be obvious. left-margin defines the distance of the left edge of the element from the left edge of its parent element. So here, this distance is 2em, which means "two em spaces". An em space is a typographical unit that is as wide as the font's height.

Refrain from absolute units!

In many CSS examples, you'll still find units like px, for example something like margin-left: 10px. While it might be tempting to use these absolute units, you should in most cases not do that. Instead, employ relative units like em or % or ch (number of characters) or vw (percentage of the viewport width). These allow for a "responsive" layout, i.e. one that works well with different screen sizes and orientations. Pixel sizes vary widely from one device to the next and do not work well if the user zooms in or out.

Note that % values always refer to the enclosing element's (i.e. the parent's) dimensions. In the case of font-size, a percentage value also refers to the font size of the parent element.

You might have wondered where the blockquote element came from in the second example above. There is a fairly simple relationship between Markdown markers and HTML elements as shown in the following table.

Markdown HTML element
Newline separated text <p>text</p>
# <h1>...</h1>
## <h2>...</h2>
### <h3>...</h3>
and so on until ###### <h6>...</h6>
- <li> as children of an <ul>, content of the li is wrapped in p elements.
1. <li> as children of an <ol>, content of the li is wrapped in p elements.
> <blockquote>...</blockquote>, content wrapped in pelements.
[text](URL) <a href="URL">text</a>
![text](URL) <img src="URL" alt="text"/>
|...| <table>...</table> with all appropriate child elements

Debugging CSS and playing with it

While it is possible to change an external CSS, switch the view in DEVONthink and then go back editing the CSS until the results please you, that is a bit tedious. A simpler approach is this:

That will open the developer tools of your browser (you might have to enable developer mode in Safari first, though). There, you'll find a "style" tab in which you can easily modify the styles for all elements individually or change the definitions in your CSS and see the changes immediately in HTML.

Delving deeper into CSS

CSS is a complicated beast, and it's getting more so every day (no thanks to Google inventing and implementing new exciting stuff). The only authoritative documentation is the one at the W3C. However, that's unwieldy stuff not meant for mere mortals. There are numerous websites out there providing easier to digest examples and reference material, notably CSS tricks and the Mozilla Developer Network.

Structure of HTML generated from Markdown

The good news is that the HTML generated from Markdown is fairly simple. It contains only HTML elements without any classes or IDs. Also, the number of elements is quite small, so you won't see the fancier stuff like aside, nav, section and the like, nor any divs wrapping elements. Basically, a HTML document generated from Markdown, has this structure:

<head>....</head>
<body>
  <h1>...</h1>
  <p>...</p>
  <h2>...</h2>
  <p>...</p>
  <blockquote><p>...</p><p>...</p</blockquote>
  other elements
</body>

where other elements can be lists or tables. All elements except head and body can repeat any number of times. Links and images are just rendered as shown above inside a p element. This simple structure makes styling HTML generated from Markdown quite easy, at least compared to a full-fledged HTML document.

Lay out the foundation

To start from scratch, I suggest to set all relevant values to reasonable defaults on the body element. This is sometimes referred to as a CSS reset. Of course, it is up to you what you consider "reasonable". Something like this might be a starting point:

body {
  margin-left: 2em;
  font-family: "Century Schoolbook", Times, serif;
  font-size: 1em;
  color: black;
  background-color: white;
  padding: 0;
  max-width: 55ch;
}

The choice of the font-family is very much a matter of taste (more so then everything else, probably). Here, I went for a serif font: "Century Schoolbook" is used if it's available. Otherwise, "Times", and if that's also missing, the default serif font. Also, I specified a font-size of 1em, which is exactly the same as the font size specified for the user agent (aka "browser") used to render the HTML. This is not strictly necessary, but it makes sure that the default text size in your HTML is the same the user specified in their browser settings.

To make the text easier to read, the maximum width of the body element (and all its children!) is set to 55ch, that is 55 average characters (loosely speaking). padding is the inner margin of an element that will remain blank, whereas margin is the space between the current element and its parent. Here, I specify a left margin of 2em so that the text does start flush left.

Fine-tune HTML elements

These styles for body define a baseline. Now you can specify differing styles for other elements to your heart's content. For example, if you'd like the first line of every paragraph to be indented by an em space, you could use

p {
  text-indent: 1em;
}

Paragraphs indented by 1em space

or if you'd want all first level headlines to have a red underline

h1 {
  text-decoration: underline red;
}

H1 headings underlined with red

or if you'd want all third level headings to appear in uppercase

h2 {
  text-transform: uppercase;
}

Uppercased H2 headings

The latter, though, is probably not a very good idea – firstly, all uppercase letters are more difficult to read, secondly many readers will think that you're screaming at them. And thirdly, the text looks fundamentally different than what you see in your Markdown document.

Styling block quotes

You've already seen a short example that made quotes stand out from the regular text. Another, more advance one is this:

blockquote {
  margin-left: 2em;
  border-left: 1px red solid;
  background-color: lightgrey;
}

First attempt at blockquote styling

Now, if you look at that in the rendered view, you might think that it leaves room for improvement. And it certainly does: first, the background color starts at the left side of the block, but the text begins 2ems to the right of that. It would be nicer if only the text would have a different background color, not the whole block including the margin.

Then the first line of text is indented by 1em. That happens because it is contained in a p element, and all p elements are indented. But we don't want that for those paragraphs inside a blockquote:

blockquote > p {
  text-indent: 0;
}

Here, blockquote > p selects only those paragraphs that are immediate children of a blockquote element, and text-indent: 0 resets the indentation of their first line so they're flush left. Now you can use the same selector to change the background: Remove the setting for background-color from the blockquote style and add it to the style for the blockquote's paragraph children like this

margin-left: 1em;
background-color: lightgrey;

Blockquote with white margin between text and red line

That gives you a white margin of 1em between the red vertical bar at the left and the grey background of the blockquote.

What about fonts?

If you want different fonts in your running text and your headline, you have to specify a different font-family for the headlines:

h1,
h2,
h3,
h4 {
  font-family: Avenir, Helvetica, "sans serif";
}

Avenir for first and second level headings

This font-family applies to headlines level 1 through 4. Note that font names containing spaces must be enclosed in double quotes. The selector is an example for specifying the same style for several HTML elements: just list the elements (or even more complicated selectors like blockquote > p) separated by commas.

Usually, the default font sizes for the different headings are ok. If you want to change those, use the attribute font-size and specify an em value, for example 2.0em for a h1. Do not use pixels or other absolute values here: The person viewing your HTML might have chosen their own preferred font size in the browser settings and specifying absolute values for font sizes plays havoc with these settings.

Other font attributes you might want to use are font-weight to specify light or bold variants, text-decoration and text-transform.

What about colors?

The preceding samples used named colors. While that might seem natural (at least for English speaking people), it is a bit awkward. What if you want a particular type of light green with a blue tint? It would be a challenge to even come up with a single english word to describe that color. And you couldn't be sure that your browser (or any browser) understands what you mean. Therefore, you can also specify colors by three hexadecimal digits like #fff (for white) or #f00 (for a dark red) or by six of them (#ffffff or #ff0000). These values represent values for red, green and blue components. So instead of going with the lightgrey understood by browsers, you might want to use #eee which is more a dirty white.

Other ways to specify colors are using rgb(red, green, blue) where the values in parenthesis are numbers between 0 and 255 or hsl(hue, lightness, saturation). MDN describes these variants in an article on color values.

How to center elements

Sometimes you might want to center certain elements, for example tables. Firstly, in many cases that only makes sense if the element is not as wide as the surrounding ones. So you should set a width value for your element that is smaller then the body's width. After that, simply set both margin-left and margin-right to auto. That makes sure that both margins are equally wide and that the white space is evenly distributed to the left and right of your element – effectively centering it. For a table, you'd for example write

table {
  width: 45ch;
  margin-left: auto;
  margin-right: auto;
}

Sizing and styling images

Without styling, an image will be displayed as large as possible. If it is smaller than the enclosing element (i.e. body), it will be made as wide as the body element is. If it's smaller, it will be displayed in its original size.

Default image display

The image height will be set to respect the original aspect ration and avoid distortion. There are ways to specify image width and height in the Markdown document itself, but I'd advise against it: It is a lot easier to style (and size) all images the same way with CSS. So,

img {
  width: 50%;
}

sets the width of all images to half of their parent element.

Image scaled to half the width of its parent

If you try that out, you'll notice that an image in the middle of a paragraph looks a bit weird since it interrupts the flow of the text in a funny way.

The reason for that is that an img element is rendered "inline", like text. Since it takes up a lot more vertical space than a line of text, the text flow is interrupted.

One way to remedy this is make sure images are rendered differently:

img {
  display: block;
}

Image displayed as block

That makes sure that images behave like paragraphs, i.e. they force line breaks before and after them.

Another, more visually pleasing way to style images is to make text flow around them as described below.

You might want to add a small shadow at the right and bottom margins of the image to make it stand out slightly:

img {
  box-shadow: 0.3em 0.3em 0.3em #ddd;
}

does just that. Explaining the values in detail would take up too much space here, though. You might want to read about box-shadow over at MDN and play around with the parameters.

Flowing text around images

In many cases, it looks nicer if the text continues next to the image instead of having a wide blank area around the image. You can achieve this by "floating" the image:

img {
  float: left;
}

Image floated to the left

makes the image stay at the left margin of the document, while the text continues at its right side. You should set the top, right, and bottom margin for the img element, too, so that the text does not crowd it.

Image floated to the left with margins

Instead of having the text flow around the image at the right side, you can set float to right so that the image appears at the document's right margin and the text to the left of it.

Styling tables

Styling tables is more demanding than styling other elements. You first have to understand table structure: a table element consists of a head and a body (thead and tbody). Both of them in turn are made up of row (tr) elements. And every row consists of th (for a table head) or td elements which are finally defining the table cells themselves. So the whole thing looks like that:

<table>
  <thead>
    <tr>
      <th>...</th>
      <th>...</th>
      ...
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>...</td>
      <td>...</td>
      ...
    </tr>
    <tr>
      <td>...</td>
      <td>...</td>
      ...
    </tr>
    ...
  </tbody>
</table>

You've just seen how to apply a very basic styling (i.e. centering) to the whole table. Another, very basic amendment would be to set the background color for the table head:

tr th {
  background-color: #d0ffd0; /* a light blue */
}

Default table styling without borders

One detail many people want are lines dividing the columns and rows. That, however, is not something you can set for the whole table. Instead, you have to specify a border for the td and th cells (i.e. those in the body and the head of the table):

table :is(td, th) {
  border: 1px solid black;
  padding: 0.3em;
}

Here, table :is(td.th) is an abbreviation for table td, table th. The border is defined as being one pixel wide, using a solid black line. This is one of the few exceptions where a pixel dimension is ok – you just want a fine line around the cells, it doesn't really matter how big a pixel is. The padding makes sure that the text in the cells has some space to breath.

However, these settings will result in a peculiar phenomenon in that there's small white space now between the borders of the individual cells. They look a bit like windows in a building. To get rid of it, add border-collapse: collapse to the style of the table element.

Table with basic borders

Another often required feature are alternating row colors. They're fairly easy to achieve like this:

tbody tr:nth-child(even) {
  background-color: #e0e0ff; /* a lighter blue than for the header */
}

Table with alternating row colors

This will display every second row with a light blue background, starting with the second row. The :nth-child pseudo-class is a bit tricky, though. If you have trouble with it, try using :nth-of-type instead.

If you want to start it with the first row, use even instead of odd in the above selector. If you want different colors for every second row, combine these rules like so:

tbody tr:nth-child(even) {
  background-color: lightblue;
}
tbody tr:nth-child(odd) {
  background-color: lightgreen;
}

There are a lot of other "pseudo classes", so it's worth checking them out on MDN. Instead of even or odd you can specify more complicated rules like 4n+1 to select the rows 1, 5, 9, 13 etc. In fact, even is the same as 2n and odd is 2n+1.

Numbered headings

By default, headings appear in HTML just as you typed them in your Markdown document. Especially in scientific publications, some kind of systematic numbering is often required. You can of course provide that by simply adding the number to your heading. However, that requires a lot of manual work and more so whenever you add, move or delete parts of the document.

CSS offers a much simpler way to render systematically numbered headlines with its concept of "counters". You can read all about counters at MDN. The following examples are just a very limited demonstration of their abilities.

Let's assume a Markdown document like this

# First heading

# Second heading

## First sub-heading of second heading

# Third heading

## First sub-heading of third heading

### Third heading's first sub-heading's first subheading

## Second sub-heading of third heading

# Fourth heading

Rendering that as HTML results in something like this

Unstyled Headings

Adding a counter to the headings requires three steps:

For the first level headings, you'd do it like this:

body {
  counter-reset: h1;
}
h1::before {
  counter-increment: h1;
  content: counter(h1) ". ";
}

First, you create a counter named h1 in the CSS rule for the body element. Its value is set to 0 by default. Then comes the ::before pseudo-element for the h1 element. It's different from other CSS selectors in that it creates an element (a pseudo one, though) in the h1 element just before anything that is already part of this element. So the (pseudo-HTML) for the first heading would look like <h1><before>...</before>First heading</h1>. Note that this before element does not exist! However, you can create and style it in a stylesheet like here. The rule for h1::before first increments the counter h1 with counter-increment. Since it started out with 0 because of the body rule, it will now be 1. Then this value is prepended to the content of h1 with the content: directive: counter(h1) gets the value of the counter, and ". " appends a dot and a space. After all that, the headlines look like this: First level headings with a counter

Numbered first level headings

Now adding the corresponding rules for 2nd and 3rd level headings is a lot easier:

h1 {
  counter-reset: h2;
}
h2 {
  counter-reset: h3;
}
h2::before {
  counter-increment: h2;
  content: counter(h1) "." counter(h2) ". ";
}
h3::before {
  counter-increment: h3;
  content: counter(h1) "." counter(h2) "." counter(h3) ". ";
}

The only new things here are the content: definitions for the level 2 and 3 headings and the counter-resets: Whenever a new level 1 heading appears, the h2 counter must be reset so that the next level 2 heading is numbered with 1 again. The same holds for a new level 2 heading that must reset the h3 counter.

All headings numbered

If you now move the line "# Second Heading" down to just before "# Third heading", you'll get this HTML:

Second heading moved

As you can see, the former "2.1 First sub-heading of second heading" has now become "1.1 First sub-heading of second heading", since it's now following the first h1 element, no longer the second one.

If you delete the line "# Second Heading" completely, the numbering for all subsequent headings will be adjusted:

Heading 2 deleted

CSS counters can not only be used for headings but also for lists, footnotes and links. You're not limited to Arabic numerals but can define your own counter-style.

What about dark mode?

Until now, all examples were assuming that the document is displayed as dark text on a white background. That would look weird on a device set to dark mode, where text should be light on a dark background. But fortunately, CSS is able to determine if the device is currently set to dark mode.

This is achieved with a "media selector" like so:

@media (prefers-color-scheme: dark) {
  ... style definitions go here;
}

The simplest style definition for dark mode would be something like

body {
  background-color: black;
  color: white;
}

and you have to make sure to put this inside the curly braces following the media selector like so:

@media (prefers-color-scheme:dark) {
  body {
    background-color: black;
    color: white;
  }
}

This is just the most basic setting. If you use a more complex color scheme, you need to verify that it is rendered nicely in dark mode. For example, all but the lightest shades of gray might be difficult to recognize.

If you're using borders, you might want to make them wider in dark mode since light on dark is more difficult to see than dark on white. Also, a font with very fine character stems might not be a good idea for dark mode, since these stems are harder to recognize.

Whatever special styling you want to apply, make sure it goes inside the media selector's curly braces.

Of course, if you prefer dark over light mode anyway, you could set up the main part of your CSS so that it works well on a dark background. And then use

@media (prefers-color-scheme:light) {
    body {
    background-color: white;
    color: black;
  }
}

as a starting point for a light mode style sheet.