9.6 Custom blocks (*)

Section 2.7 of the bookdown book mentioned how we can use custom blocks in R Markdown to customize the appearance of blocks of content. This can be a useful way to make some content stand out from your report or book, to make sure that your readers take away the key points from your work. Examples of how these blocks could be used include:

  • display a warning message to make sure users are using up-to-date packages before running your analysis;

  • add a link at the beginning of your document to your GitHub repository containing the source;

  • highlight key results and findings from your analysis.

In this section, we will explain how to create your own custom blocks for both PDF and HTML output. They can both use the same formatting syntax in the R Markdown document, but require different configurations.

9.6.1 Syntax

The syntax for custom blocks is based on Pandoc’s fenced Div blocks. Div blocks are very powerful, but there is a problem at the moment: they mainly work for HTML output and do not work for LaTeX output.

Since version 1.16 of the rmarkdown package, it has been possible to convert Div blocks to both HTML and LaTeX. For HTML output, all attributes of the block will become attributes of the <div> tag. For example, a Div can have an ID (after #), one or multiple classes (class names are written after .), and other attributes. The following Div block

::: {#hello .greeting .message style="color: red;"}
Hello **world**!
:::

will be converted to the HTML code below:

<div id="hello" class="greeting message" style="color: red;">
  Hello <strong>world</strong>!
</div>

For LaTeX output, the first class name will be used as the LaTeX environment name. You should also provide an attribute named data-latex in the Div block, which will be the arguments of the environment. This attribute can be an empty string if the environment does not need arguments. We show two simple examples below. The first example uses the verbatim environment in LaTeX, which does not have any arguments:

::: {.verbatim data-latex=""}
We show some _verbatim_ text here.
:::

Its LaTeX output will be:

\begin{verbatim}
We show some \emph{verbatim} text here.
\end{verbatim}

When the block is converted to HTML, the HTML code will be:

<div class="verbatim">
We show some <em>verbatim</em> text here.
</div>

The second example uses the center and minipage environments to display some text in a centered box of half of the page width.

:::: {.center data-latex=""}

::: {.minipage data-latex="{.5\linewidth}"}
This paragraph will be centered on the page, and
its width is 50% of the width of its parent element.
:::

::::

Note that we nested the minipage block in the center block. You need more colons for a parent block to include a child block. In the above example, we used four colons (you can use five or more) for the center block. The two blocks will be converted to the LaTeX code below:

\begin{center}
\begin{minipage}{.5\linewidth}
This paragraph will be centered on the page, and
its width is 50\% of the width of its parent element.
\end{minipage}
\end{center}

It is up to the user to define the appearance of their <div> blocks via CSS for the HTML output. Similarly, for LaTeX output, you may use the command \newenvironment to define the environment if it has not been defined, or \renewenvironment to redefine an existing environment in LaTeX. In the LaTeX definitions, you can decide on the appearance of these blocks in PDF. These customizations will normally be contained in their own files such as style.css or preamble.tex, and then included within the YAML options:

---
output:
  html_document:
    css: style.css
  pdf_document:
    includes:
      in_header: preamble.tex
---

Next we will demonstrate a few more advanced custom blocks that use custom CSS rules and LaTeX environments. You may find an additional example in Section 5.8, in which we arranged multiple blocks in a multi-column layout.

9.6.2 Adding a shaded box

First, we show how to include content in a shaded box. The box has a black background with an orange frame with rounded corners. The text in the box is in white.

For HTML output, we define these rules in a CSS file. If you are unfamiliar with CSS, there are plenty of free online tutorials, e.g., https://www.w3schools.com/css/.

.blackbox {
  padding: 1em;
  background: black;
  color: white;
  border: 2px solid orange;
  border-radius: 10px;
}
.center {
  text-align: center;
}

For LaTeX output, we create a new environment named blackbox and based on the LaTeX package framed, with a black background and white text:

\usepackage{color}
\usepackage{framed}
\setlength{\fboxsep}{.8em}

\newenvironment{blackbox}{
  \definecolor{shadecolor}{rgb}{0, 0, 0}  % black
  \color{white}
  \begin{shaded}}
 {\end{shaded}}

We used the framed package in this book because it is fairly lightweight, but it is not possible to draw a colored frame with rounded corners with this package. To achieve the latter, you will need more sophisticated LaTeX packages such as tcolorbox (https://ctan.org/pkg/tcolorbox), which offers a set of very flexible options for creating shaded boxes. You can find many examples in its documentation. The LaTeX environment below will create a shaded box of similar appearance to the above CSS example:

\usepackage{tcolorbox}

\newtcolorbox{blackbox}{
  colback=black,
  colframe=orange,
  coltext=white,
  boxsep=5pt,
  arc=4pt}

Now we can use our custom box in both PDF and HTML output formats. The source code of the box is:

:::: {.blackbox data-latex=""}
::: {.center data-latex=""}
**NOTICE!**
:::

Thank you for noticing this **new notice**! Your noticing it has
been noted, and _will be reported to the authorities_!
::::

The output is:

NOTICE!

Thank you for noticing this new notice! Your noticing it has been noted, and will be reported to the authorities!

9.6.3 Including icons

We can make custom blocks even more visually appealing by including images in them. Images can also be an effective way to convey the content of the block. For the following example, we assume that we are working within a directory structure below, which is a simplified version of what is used to build this book:

directory/
├── your-report.Rmd
├── style.css
├── preamble.tex
└── images/ 
      └── ├── important.png
          ├── note.png
          └── caution.png

We show the source code and output of the example before we explain how everything works:

::: {.infobox .caution data-latex="{caution}"}
**NOTICE!**

Thank you for noticing this **new notice**! Your noticing it has
been noted, and _will be reported to the authorities_!
:::

The output is:

NOTICE!

Thank you for noticing this new notice! Your noticing it has been noted, and will be reported to the authorities!

For the HTML output, we can add an image to the box through the background-image property in CSS. We insert the image into the background, and add enough padding on the left-hand side to avoid the text overlapping with this image. If you are using local images, the file path to the images is provided relative to the CSS file. For example:

.infobox {
  padding: 1em 1em 1em 4em;
  margin-bottom: 10px;
  border: 2px solid orange;
  border-radius: 10px;
  background: #f5f5f5 5px center/3em no-repeat;
}

.caution {
  background-image: url("images/caution.png");
}

Note that we used two class names, .infobox and .caution, on the outer block. The infobox class will be used to define the shaded box with a colored border, and the caution class will be used to include the image. The advantage of using two classes is that we can define more blocks with different icons without repeating the setup of the shaded box. For example, if we need a warning box, we only need to define the following CSS rule without repeating rules in .infobox:

.warning {
  background-image: url("images/warning.png");
}

Then you can create a warning box with the Markdown source code below:

:::: {.infobox .warning data-latex="warning"}

Include the actual content here.

::::

For the PDF output, we can create an infobox environment based on the blackbox environment defined in the previous example, and add the icon to the left side of the box. There are multiple ways of including images in a LaTeX environment. Here is only one of them (it does not precisely reproduce the box style defined in the CSS above):

\newenvironment{infobox}[1]
  {
  \begin{itemize}
  \renewcommand{\labelitemi}{
    \raisebox{-.7\height}[0pt][0pt]{
      {\setkeys{Gin}{width=3em,keepaspectratio}
        \includegraphics{images/#1}}
    }
  }
  \setlength{\fboxsep}{1em}
  \begin{blackbox}
  \item
  }
  {
  \end{blackbox}
  \end{itemize}
  }

Below we show more example blocks with different icons:

NOTICE!

Thank you for noticing this new notice! Your noticing it has been noted, and will be reported to the authorities!

NOTICE!

Thank you for noticing this new notice! Your noticing it has been noted, and will be reported to the authorities!

NOTICE!

Thank you for noticing this new notice! Your noticing it has been noted, and will be reported to the authorities!

NOTICE!

Thank you for noticing this new notice! Your noticing it has been noted, and will be reported to the authorities!

Alternatively, you may use the LaTeX package awesomebox to generate boxes with icons in the PDF output. This package gives you a much larger number of icons to choose from. We give a brief example below: please refer to the package documentation for the possible LaTeX environments and their arguments.

---
title: Awesome Boxes
output:
  pdf_document:
    extra_dependencies: awesomebox
---

A note box:

::: {.noteblock data-latex=""}
Thank you for noticing this **new notice**! Your noticing it has
been noted, and _will be reported to the authorities_!
:::

We define an R function `box_args()` to generate the arguments
for the box:

```{r}
box_args <- function(
  vrulecolor = 'white',
  hrule = c('\\abLongLine', '\\abShortLine', ''),
  title = '', vrulewidth = '0pt',
  icon = 'Bomb', iconcolor = 'black'
) {
  hrule <- match.arg(hrule)
  sprintf(
    '[%s][%s][\\textbf{%s}]{%s}{\\fa%s}{%s}',
    vrulecolor, hrule, title, vrulewidth, icon, iconcolor
  )
}
```

Pass some arguments to the `awesomeblock` environment through
an inline R expression:

::: {.awesomeblock data-latex="`r box_args(title = 'NOTICE!')`"}
Thank you for noticing this **new notice**!

Your noticing it has been noted, and _will be reported to
the authorities_!
:::