Animating the Jolla Theme From the Postcards R Package

Recently, I came across this great tutorial by Connor Rothschild, where he teaches you how to animate the Hugo Academic theme for a blogdown website. Inspired by this post, I wanted to apply similar animations to my personal website, which was built using the postcards R package.1 Below is what the resulting animated theme looked like:

If you don’t care about how I implemented this and just want to apply an animated version of the jolla theme (screenshot above), you can install a forked copy of the postcards R package from my repo using the R command remotes::install_github("albertkuo/postcards@main"). Then you can apply the animated jolla theme by replacing postcards::jolla with postcards::jolla_anim in your R markdown preamble.

However, if you’re interested in how I customized a theme from the postcards R package to add animations, keep reading!

Step 1: Create a new pandoc template

Since there is no native way to add your own template to the postcards R package (as far as I know), we will need to hack the package a little bit.

Step 1.1: Fork package and create new pandoc template

First, fork the repository for the postcards R package and clone the forked repository to your computer. If you don’t understand what this means, you may need to briefly read the documentation on github. Essentially, this creates a copy of the R package, which we will modify and then install locally.

Next, in the cloned repository on your computer, navigate to inst/pandoc_templates. You will see that there are already a couple of files, each corresponding to a template from the postcards package (e.g. jolla, onofre, solana, etc.). Create a copy of the jolla.html file and name it jolla-anim.html. This will be the pandoc template that we will use for our new, animated theme.

Step 1.2: Edit R functions

Now navigate to R/. Edit the create.R file by adding jolla-anim wherever you see jolla. This adds an extra entry for the new pandoc template we are creating.2 Specifically, edit the following lines to look like what I’ve printed here:

img_table <- as.list(
      system.file("img",
                  c("tobi.jpg", "tobi.jpg", "xiang.jpg", "frank.jpg", "herzl.jpg", "sigridur.jpg"),
                  package = "postcards"))

    names(img_table) <- c("jolla", "jolla-anim", "jolla-blue", "trestles", "onofre", "solana")
template_table <- as.list(c("jolla", "jolla-anim", "jolla-blue", "trestles", "onofre", "solana"))
  names(template_table) <- c("Jolla", "Jolla Anim", "Jolla Blue", "Trestles", "Onofre", "Solana")

Also under R/, edit the postcards.R file and add our new theme by inserting the following code chunk. This creates a function for our jolla_anim theme. You can put this code chunk under the jolla function.

#' @rdname jolla
#' @export
jolla_anim <- function(css = NULL, includes = NULL, ...) {
    get_template("jolla-anim", css, includes, ...)
}

Finally, create a copy of the jolla folder under inst/rmarkdown/templates/, rename it jolla-anim, and modify its skeleton.Rmd (under jolla-anim/skeleton/) to use postcards::jolla_anim in the preamble.

Step 1.3: Test changes

What we’ve done so far is we’ve created a duplicate of the jolla theme, named it jolla_anim, and modified the functions in the R package so that this new theme is recognized by the package.

To test whether this worked, install the devtools R package and run the following commands:

  1. devtools::document(/path/to/your/postcards/package) - this function exports the jolla_anim function to NAMESPACE so that the theme can be found when you knit the Rmd
  2. devtools::install(/path/to/your/postcards/package) - this function installs the modified version of the postcards R package in your computer, which you will have to run every time you make changes to the template or the CSS file (more on this later)

Now try applying the jolla_anim theme to your postcards website by editing the part of the R markdown preamble shown below. You should see that the Rmd knits successfully and outputs an html file that looks like the jolla theme. If you run into issues, try uninstalling the original version of the postcards R package and reinstalling your modified local version.

output:
  postcards::jolla_anim

Step 2: Create a custom css file

OK so we have successfully created a theme called jolla_anim and added it to the R package. Now, as our theme name suggests, we want to customize the jolla theme with animations.

I will add two types of animations, a fade-in and a slide. To add these animations, we will need to modify our pandoc template slightly to label different sections of the pandoc template and also add a CSS file to animate those sections.

Warning: I’m a CSS noob, so some of my terminology may not be accurate in the following section and I can’t promise that what I’m doing follows best practices. However, all of the CSS and html stuff is covered in more detail in Connor’s blog post, so feel free to check it out.

Step 2.1: Add a fade-in animation

First, let’s fade in our profile photo. In our pandoc template (inst/pandoc_templates/jolla-anim.html), see where it says <img src="$image$" style="height:10rem" class="rounded-circle">? This is the profile picture. We can wrap it in a <div> section, which I will label with the class name “profile-pic”:

<div class="profile-pic">
  <img src="$image$" style="height:10rem" class="rounded-circle">
</div>

Now we want to create a CSS file to animate this <div> section. To do so, navigate to inst/ and create a folder called css. Under inst/css, create a file called custom-styles.css. In the CSS file, add the following:

@keyframes fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

.profile-pic {
  opacity: 0;
  animation: fade-in 1s forwards;
  animation-delay: 200ms;
}

The @keyframes code chunk creates the “fade-in” animation. The .profile-pic refers to the <div> class we just created in our pandoc template and in line 12, we bind the animation “fade-in” to this element. The animation-delay in line 13 tells the browser how long to wait before we begin the animation.

One last thing to do before the animation will work – we need to link our custom CSS file to our pandoc template. To do so, open the R/postcards.R file and edit the jolla_anim function to call the CSS file. Your function will now look like the following:

jolla_anim <- function(css = system.file("css",
                                         "custom-styles.css",
                                         package = "postcards")
                       , includes = NULL, ...) {
    get_template("jolla-anim", css, includes, ...)
}

If you look at the pandoc template (the jolla-anim.html file), you’ll see that it’s already been configured to read in whatever CSS file you pass as a function parameter. This is done with the line <link rel="stylesheet" href="$css$" $if(html5)$$else$type="text/css" $endif$/>.

Now to test whether your fade-in animation worked, re-install your package with devtools::install and re-knit your markdown. It should look something like the following (note that the gif below loops, but your picture should only fade-in once):

To apply the fade-in animation to the other elements of the page, you would just repeat these steps: create <div> sections with class names in the pandoc template and bind them to the fade-in animation in the CSS file. The only thing worth noting is that for the title (your name), you can apply a class name directly with <h1 class="biography-title"> in the pandoc template. Once you’re done, your CSS file would look something like the following:

@keyframes fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

.profile-pic {
  opacity: 0;
  animation: fade-in 1s forwards;
  animation-delay: 200ms;
}

.biography-title {
  opacity: 0;
  animation: fade-in 1s forwards;
  animation-delay: 300ms;
}

.main-content {
  opacity: 0;
  animation: fade-in 1s forwards;
  animation-delay: 400ms;
}

Step 2.2: Add a simultaneous fade-in and slide animation

The other type of animation we will apply is a simultaneous fade-in and slide, which we will call “slide-from-bottom.” We will apply this to a <div class="button-row"> section, wrapped around the row of buttons in our pandoc template. To add this animation, insert the following to your CSS file:

@keyframes slide-from-bottom {
  0% {
    transform: translateY(100%);
    opacity: 0;
  }
  100% {
    transform: translateY(0%);
    opacity: 1;
  }
}

.button-row {
  opacity: 0;
  animation: slide-from-bottom 2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  animation-delay: 400ms;
}

Note that this “slide-from-bottom” animation is just an extension of the “fade-in” animation. We change the opacity like in the “fade-in” animation and additionally change the position of the element with transform and translateY.

The cubic-bezier easing function on line 14 controls the acceleration rate of the transition (e.g. the slide-in speed can accelerate or slow down at the beginning or the end of the transition). You can find different easing functions at https://easings.net/. The one I’ve chosen here is the “easeOutExpo” function.

Step 2.3: Putting it all together

To allow animations to display properly across different browsers, copy your CSS file to the CSS Autoprefixer and use its output in your final CSS file. You will see that it adds additional code to make your animations more robust across browsers.

All of these animations and CSS stuff are again described in more detail in Connor’s blog post – you would just have to do a little bit of translation since his post is about the Hugo Academic theme. If you get stuck, you can also copy or look at the files in my github repo.

With your pandoc template and CSS file, you can also modify/customize other visual elements of the page as you see fit. For example, I changed the link color to orange in my CSS file and the button style to a lighter gray in the pandoc template. Just remember that every time you make a change, you will need to re-install the R package again with devtools::install.

Finally, we get our animated jolla theme!


  1. Yes, I realize that the whole point of using the postcards R package is to simplify site maintenance, but I can’t seem to stop myself from tweaking themes…↩︎

  2. To be honest, I don’t know whether this step is strictly necessary, but it feels like the more complete thing to do if we’re trying to add a new theme to the package.↩︎