Authoring HTML Articles with the markdown Package

Yihui Xie

2023-12-05

The R package markdown can be used to generate lightweight HTML documents with minimal default CSS styles. If you are not familiar with this package, you may read the introduction1 first to learn the basics. The HTML format is highly customizable if you know some CSS and JavaScript. This article demonstrates an HTML article format by introducing external CSS and JavaScript to the base format.

1. Get started

To write an HTML article, you may use the following metadata variables (css and js):2

output:
  markdown::html_format:
    meta:
      css: ["default", "@xiee/utils/css/article.min.css"]
      js: ["@xiee/utils/js/sidenotes.min.js,appendix.min.js"]

The default CSS is the default style provided by this package. Beyond that, only the CSS file article.css3 is required.

Since we are going to demonstrate all elements, we have loaded both JS files for this document.

2. The overall style

The maximum width of the article body is 800px. For larger screens, this means there will be extra space in the left/right margin, where we can place auxiliary information, such as the TOC and footnotes. On smaller screens, the side content will be collapsed into the body.

The article frontmatter, body, and optionally the appendix are placed in separate boxes.

The default typeface is sans-serif, and you can customize it by supplying an external CSS file (via the css meta variable) or just embedding CSS in the document body, e.g.,

body {
  font-family: Palatino, "Book Antiqua", Georgia, serif;
  font-size: 1em;
}

3. Side elements

The TOC and footnotes are automatically placed in the margin if space permits. You can also write arbitrary content in the margin via a fenced Div.

3.1 The TOC

The TOC is sticky on the left side as you scroll down the article. If you do not like this behavior, you may cancel it via CSS:

#TOC {
  top: unset;
}

3.2 Footnotes

Footnotes are moved to the right side. When you move your cursor over a footnote number in the body, the footnote will be moved next to your cursor. This can be convenient when you have multiple footnotes on a line, since you do not need to look for a specific footnote in the margin.

3.3 Arbitrary sidenotes

You can write anything in the margin by using a fenced Div with the classes .side and .side-left or .side-right.

Notice

Here is a note on the left side. Anything permitted by law is permitted here. Math? No problem!

$$e^{i\theta}=\sin{\theta}+i\cos{\theta}$$

When you have this sidenote “hammer”, I’m sure you will hit a lot of nails into the margin, even if you do not have to.

And here is a note on the right side. Seriously, we should let commonmark’s authors know that fenced Divs really deserve first-class support! They can make Markdown infinitely customizable.

::: {.side .side-left}
**Anything** on the left.
:::
::: {.side .side-right}
_Anything_ on the right.
:::

4. Body elements

Inside the article body, you can write a few special elements.

4.1 Full-width elements

When an element is wider than the article body, you can show it in its full width by enclosing the element in a fenced Div with the class .fullwidth, e.g.,

::: {.fullwidth}
![text](path/to/image)
:::

Sunspots

If you use R Markdown, you can generate a wide plot or table from an R code chunk, e.g.,

::: {.fullwidth}
```{r}
#| sunspots, echo=FALSE, fig.dim=c(14, 4),
#| fig.cap='Monthly mean relative sunspot numbers from 1749 to 1983.'
par(mar = c(4, 4, .1, .1), bg = 'lightgoldenrodyellow', fg = 'red', las = 1)
plot(sunspots, col = 'red', panel.first = grid())
```
:::

4.2 Left/right quotes

Whenever you find that you are on the side of the majority, it is time to pause and reflect.

Mark Twain

Sometimes you may want to add a quote but do not want it to take the full width in the body. You may use a fenced Div with the class .quote-left or .quote-right.

Despite the class names, the content does not have to be a quote. If you do want a quote, just use the blockquote syntax >, e.g.,

::: {.quote-right}
> This is a boring quote.
>
> ---Someone
:::

4.3 Margin embedding

mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1

You can embed elements on the left or right margin using a fenced Div with the class .embed-left or .embed-right. These elements will float to the left or right and exceed the margin by about 200px, which can save some space in the article body. You can use the extra space to explain the embedded element with text.

We have embedded a table of the first 4 rows of the mtcars data on the right margin. The table was generated via knitr::kable().

Appendix

The appendix starts with a heading that has the attribute .appendix, e.g., ## Appendix {.appendix}.

A. Technical notes

It’s quite simple to move an element into the margin using CSS. For example, the .side-right class in this article is roughly defined as:

.side-right {
  width: 200px;
  float: right;
  margin-right: -200px
}

That basically means the width of the element is 200px and it floats to the right. Now its right side will touch the right margin of its parent element (the article body). What we need to do next is move it further to the right by 200px (i.e., its width), which is done by the -200px right margin. Remember, a positive right margin in CSS moves an element to the left, and a negative right margin moves it to the right.

  1. The package vignette: vignette("intro", package = "markdown")

  2. If you are curious about what the leading @ characters mean in the values, please read the section “YAML metadata” in the introduction vignette of this package.

  3. The actual file extension is .min.css, which means a minified version of the .css. The CSS is minified to make it load faster. If you do not care, you can definitely use the .css extension. The same thing applies to .min.js vs .js.