An anatomy of R Markdown with minimal applications

Yihui Xie @ ASA stat computing session

2023-01-10

About this talk

It is not the man who has too little that is poor, but the one who hankers after more.

Letters from a Stoic (Lucius Annaeus Seneca)

This talk is intended for

I’ll accompany this talk with a blog post later: https://yihui.org/en/2023/01/minimal-r-markdown/


The basic idea

Source document -> Markdown output -> Target document

Examples:

We only talk about the second step today, i.e., converting .md.


A minimal HTML document

<html>
  <head>
    <title>Document Title</title>
  </head>
  <body>
  Document body
  </body>
</html>

Right-click on a web page, then “View Page Source”. You will see the HTML source code of a page.


A minimal LaTeX document

\documentclass{article}
% preamble
\begin{document}
% body
\end{document}

Basic components of a document

Markdown also has these components:

---
title: Document Title
author: Yihui Xie
---

Document body.

The markdown package

Note that I’m talking about the markdown package, not rmarkdown! Usually “R Markdown” refers to the latter, which is much more powerful and widely known.

A little bit history about markdown:


Reviving the markdown package

The development of the markdown package has almost stopped since 2014, but it can’t simply die on CRAN because many packages still use it.

In October last year, I got CRAN emails regarding some maintenance problems. I couldn’t afford time to fix the endless C problems any more in the deprecated sundown library, so I deleted all C code and switched to Jeroen Ooms’s commonmark.

I no longer have to maintain any C code! Thanks, Jeroen!


I’m using the development version today

You can install the dev version of markdown with:

install.packages('markdown', repos = c(
  rstudio = 'https://rstudio.r-universe.dev',
  CRAN = 'https://cloud.r-project.org'
))

Generate the document body

markdown::mark() is based on commonmark. The latter can help us generate the document body.

cat(markdown::mark('Hello **world**!', format = 'latex'))
#> Hello \textbf{world}!
cat(markdown::mark('Hello **world**!', format = 'html'))
#> <p>Hello <strong>world</strong>!</p>

Generate the full document

We need a template, into which we insert the body and metadata. For example:

<html>
  <head>
    <title>$title$</title>
    <style type="text/css">$css$</style>
  </head>

  <body>$body$</body>
</html>

Or a LaTeX template:

\documentclass{$documentclass$}

\title{$title$}
\author{$author$}
\date{$date$}

\begin{document}
\maketitle
$body$
\end{document}

From Markdown to a full HTML/LaTeX document

  1. Read the YAML metadata in Markdown and store them in a list of variables, e.g., title, author, etc.

  2. Convert the Markdown body to the target format and store in the body variable

  3. Read the template, and substitute the variable names with their values in above steps


A toy implementation

#' @param text Markdown input
#' @param meta A list of metadata
#' @param template The template text
to_html <- function(text, meta = list(), template = '') {
  meta$body <- commonmark::markdown_html(text)
  output <- template  # initialize output to be template text
  for (i in names(meta)) {
    variable <- paste0('$', i, '$')  # e.g., $title$
    output <- gsub(variable, meta[[i]], output, fixed = TRUE)
  }
  output
}

Try it out

res <- to_html(
  'Hello **world**!', meta = list(title = 'Try it'),
  template = '<title>$title$</title>
<body>\n$body$</body>'
)
cat(res)
<title>Try it</title>
<body>
<p>Hello <strong>world</strong>!</p>
</body>

Now you know how R Markdown works internally

---
title: Try it
---

Hello **world**!
<html>
  <head><title>Try it</title></head>
  <body><p>Hello <strong>world</strong>!</p></body>
</html>

Demos

---
output:
  markdown::html_format: 
    options: ...
    meta: ...
  markdown::latex_format: ...
---

Turn an HTML page into a presentation

Two lines of CSS code:

html { scroll-snap-type: y mandatory; }
.slide { min-height: 100vh; scroll-snap-align: start; }

The key technique: CSS Scroll Snap

This may be the world’s simplest method to create HTML slides.


Also some JavaScript

JavaScript is a lot of fun to me. You can manipulate anything on a web page (including your bank account balance—of course—only locally in your browser).

Example: implement keyboard shortcuts

document.addEventListener('keyup', (e) => {
  e.key === 'f' && document.documentElement.requestFullscreen();
  e.key === 'o' && document.body.classList.toggle('overview');
  e.key === 'm' && document.body.classList.toggle('mirrored');
});

Thanks!