Ninja scaffolding for {xaringan}

A slide split into four sections, with a title row, footer row and two main-body rows, the lower of which is split in two vertically.

Example of modified Ninjutsu for ‘scaffolding’ a {xaringan} slide.

tl;dr

Emi Tanaka‘s Ninjutsu CSS for {xaringan} breaks slides into ’cells’, which are useful for arranging plots, tables, etc. I’ve been experimenting with Emi’s CSS to create my own layouts.

Slide themes

The {xaringan} package by Yihui Xie – an implementation of remark.js – lets you create reproducible slides with R.

You can create your own themes for {xaringan} by supplying some CSS. Yihui has encouraged users to add their themes to the package itself.

Tip: if you don’t know CSS, Garrick Aden-Buie’s {xaringanthemer} package lets you write R code and will generate the corresponding CSS for you.

Slide layouts

I was given a brief to create slides with a particular layout of page elements (plots, tables, text). How could I create a ‘scaffold’ in {xaringan} into which I could place the page elements?

Fortunately, Emi Tanaka1 created Ninjutsu2: CSS classes for splitting your page into columns and rows. This is now built into {xaringan} along with her Kunoichi theme3.

For example, the split-1-2-1 class from Ninjutsu splits the slide into three columns that are 25%, 50% and 25% of the total page width (hence ‘1-2-1’).

.split-1-2-1>.column:first-of-type {
  width: 25%; height: 100%; position: absolute; top: 0; left: 0; 
}
.split-1-2-1>.column:nth-of-type(2) {
  width: 50%; height: 100%; position: absolute; top: 0; left: 25%;
}
.split-1-2-1>.column:nth-of-type(3) {
  width: 25%; height: 100%; position: absolute; top: 0; right: 0;
}

There’s one line of CSS for each of the three columns that the slide will be split into. In this example, the first column (first-of-type) starts from the extreme left (left: 0;), the middle column (nth-of-type(2)) starts where the first one ends (left: 25%;) and the third one (nth-of-type(3)) starts from the extreme right (right: 0;).

Demo: ninja scaffold

I adapted Emi’s CSS to create the layouts I wanted. You can:

Read the rest of this post for an explanation of how I did it.

Adapting the layout

Specifically, I wanted to define title, body and footer-bar sections for:

  • a title page
  • a main, generic content page
  • a content page that could show a table and plot next to each other, with a wider table underneath

This means I could colour them and/or fill them with content according to some additional CSS formatting.

To do this, I tweaked Emi’s split-1-2-1 class to create classes with rows (rather than columns) split into the sections I wanted.

Below are some simplified examples of what I did. You can see the original CSS in the source code of the demo I’ve put on GitHub.

Defining the classes

The title page has two rows, one is a large header area to hold a logo and the other is where the talk metadata goes (see demo):

.split-title>.row:first-of-type {
  height: 35%; width: 100%; position: absolute; top: 0; left: 0;
}
.split-title>.row:nth-of-type(2) {
  height: 65%; width: 100%; position: absolute; left: 0; top: 35%;
}

The main slide class has a title and footer section and the content goes in the large section between (see demo):

.split-main1>.row:first-of-type {
  height: 15%; width: 100%; position: absolute; top: 0; left: 0;
}
.split-main1>.row:nth-of-type(2) {
  height: 80%; width: 100%; position: absolute; left: 0; bottom: 5%;
}
.split-main1>.row:nth-of-type(3) {
  height: 5%; width: 100%; position: absolute; bottom: 0; left: 0;
}

I modified slightly this class so the large blank area is split once more (see demo):

.split-main2>.row:first-of-type { 
  height: 15%; width: 100%; position: absolute; top: 0; left: 0;
}
.split-main2>.row:nth-of-type(2) { 
  height: 40%; width: 100%; position: absolute; left: 0; top: 15%;
}
.split-main2>.row:nth-of-type(3) { 
  height: 40%; width: 100%; position: absolute; left: 0; bottom: 5%;
}
.split-main2>.row:nth-of-type(4) { 
  height: 5%; width: 100%; position: absolute; bottom: 0; left: 0;
}

Using the classes

Having defined the CSS, how do we use it?

Let’s say I’d saved this CSS into a file called custom.CSS, along with some font specifications in custom-fonts.CSS. Here’s a simplified YAML header showing how to declare these CSS files in your {xaringan} .Rmd file:

---
title: "Example Title"
output:
  xaringan::moon_reader:
    css: [default, ninjutsu, custom.css, custom-fonts.css]
---

The CSS files are listed in this order so that the later-listed files take precedence if there’s any clashes. Remember also that in-built themes don’t need the .css file extension, but you need to provide the full path for any custom CSS.

With {xaringan} you name the class at the top of a slide to actually apply it, where slides are defined by three hyphens ---.

You can see an example in the source code of the demo I put on GitHub.

Class split-main1

So, the split-main1 could be used like this:

---
class: split-main1

.row[.content[
This Is A Title
]]

.row[.content[
This is the main body area of the slide.
]]

Remember that the split-main1 class is split into three separate rows for the title, main body and footer sections. We can define what’s in each row with .row[] and then define the content inside a call to .content[].

The first .row[] call will take the style from .split-main1>.row:first-of-type in our CSS, the second will take the CSS information from .split-main1>.row:nth-of-type(2), and so on.

Note that Ninjutsu also lets you adjust the content of classes by chaining calls like .content.vmiddle[], which will make the content vertically centred in this case.

Class split-main2

The split-main2 class has an additional .row[] to define because the main body area is composed of two rows rather than one.

Here I’ve added a split-two[] call inside the second .row[] call and declared .column[] twice within it. This results the main body area containing one row the width of the page and one row split into two columns (see demo).

---
class: split-main2

.row[.content[
This Is A Title
]]

.row[.content[
This is the second row of the page. It's full-width.
]]

.row[.content[
.split-two[
.column[.content[
This is the third row, which is split in two. This is the left column.
]]
.column[.content[
This is the right-hand column of the third row.
]]
]
]]

.row[
]

What now?

I think there’s a lot of promise in this approach for making bespoke page layouts.

I’ve already used this approach at work to design a {xaringan} template to a specification, which I used to help automate the generation of a large number of reports. In particular, I used the split-main2 class to arrange a small table in the top-left, a plot in the top-right, and a full-width wide table below them.

It’s possible to create these kinds of layouts in other ways – like in {pagedown} or with fancier CSS skills. For now I’m most comfortable with {xaringan} and the features it offers, like presenter notes and presenter view. But this is probably less important if your goal is to output to PDF.

I look forward to seeing how Ninjutsu and other {xaringan} themes develop to help with page layouts for reproducible presentations.


  1. A certified great hacker.

  2. Wikipedia: ‘the strategy and tactics of unconventional warfare, guerrilla warfare and espionage purportedly practiced by the ninja’.

  3. Wikipedia: ‘a female ninja or practitioner of ninjutsu’.