Setting up your blog with RStudio and blogdown III: modify your theme
This is Part III of my series of posts about how to setup you blog with RStudio and blogdown. The other parts are:
In this post I am going to show how I modified the original theme casper-two
that I use for my blog. It is a great theme, but for sure you would like to change the menus, the appearance, the structure of the information in the blog, etc.
Content:
- Hugo internals
- Changing the main menu
- Google Analytics
- Math equations
- Custom CSS
- Altmetrics
- Show only some posts in the main page
- Fixing Youtube
Hugo internals
But before that we have to learn a little bit about how Hugo and our theme works underneath our blog. If you have a look the folder where your blog is, you will find this structure
{width=80%}
The last directory (hugo-casper-two
) contains the theme files and has this structure:
{width=80%}
The folder layouts
contains a lot of html files in HUGO format that specify how our pages are going to be rendered. Each file is a mixture of html and HUGO variables. For example, this is how the index.html
looks like
{{ partial "head" . }}
<body class="home-template"><div class="site-wrapper">
{{ partial "header" . }}
<main id="site-main" class="site-main outer" role="main">
<div class="inner">
<div class="post-feed">
{{ partial "post-list-main" . }}
</div>
<nav class="pagination" role="navigation">
{{if .Paginator.HasPrev}}
<a class="newer-posts" href="{{ .Paginator.Prev.URL }}">←
<span class="hide">Next Posts</span></a>
{{end}}
<span class="page-number"><span class="hide">Page {{ .Paginator.PageNumber }}
of {{.Paginator.TotalPages}}</span> </span>
{{if .Paginator.HasNext}}
<a class="older-posts" href="{{ .Paginator.Next.URL }}">
<span class="hide">Previous Posts</span> →</a>
{{end}}
</nav>
</div>
</main>
{{ partial "footer" . }}
</div>{{ partial "js" . }}</body></html>
Each {{ }}
command tells Hugo to do something. In particular the first one ({{ partial "head" . }}
) includes in that position the partial head.html
which is also in the layouts folder. Then it comes some HTML, another partial header.html
, etc. You can also put controls/functions for the final code (see for example the if control {{if .Paginator.HasPrev}}
) or output some variables in the code depending on the context ({{ .Paginator.Prev.URL }}
), etc. Variables and parameters are preceded by a dot. For example .Site.BaseURL` is the base URL for the site.
Have a look at how Hugo manages the content in the templates in the Hugo webpage.
Modifying the template requires to changing the files for the different layouts. But instead of modifying the original template files in the themes
folder, it is better to follow the advise here and replicate the layouts structure outside the themes
folder. Hugo will read first our own layout
folder and then the layout
folder in the original theme. For example, in this post we are going to modify index.html
, js.html
, etc. And create new layouts post-list-main.html
or shortcodes like yt.html
. So your final blog folder will look like this.
{width=80%}
Changing the main menu
This is really easy. Go to your config.toml
and you will see a number of [[menu.main]]
items in it. Change them to link to
- Category pages, showing all the posts in a category. Like this:
[[menu.main]]
name = "Publications"
url = "/categories/publications"
weight = 150
- Individual pages. A link to a particular page/post in your blog
[[menu.main]]
name = "About"
url = "/about/"
weight = 94
- External pages. A link to a external page not in your blog
[[menu.main]]
name = "My University"
url = "http://www.uc3m.es"
weight = 32
The weight
controls the order in which they appear. Higher weights mean they will appear in the leftmost positions.
Google Analytics
If you want to use Google Analytics for your blog, you can do it by specifying your Google Analytics tracking id in the config.toml
googleAnalytics = "UA-XXXXYYZZY-X"
It works great, but there is something that I didn’t like: the tracking works even in your production environment when you are running the blogdown::serve_site()
. This means that when you are writing a post, updating locally your pictures, etc., and you navigate your blog in Rstudio, it will count as visits to your blog in Google Analytics.
We are going to modify the theme so that only visits from others are counted in Google Analytics. To do that we are going to copy the file js.html
to our local layouts
folder. If you edit it you will see that the Google Analytics script is the following part:
{{ with .Site.Params.googleAnalytics }}
{{ "<!-- Google Analytics -->" | safeHTML }}
<script>
var _gaq=[['_setAccount','{{ . }}'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
{{ end }}
But we are going to tell Hugo to include this script only if the webpage is in the final domain, that is, if we build the final deployment version of our blog. To do that you have to change the above code to
{{ $baseurl := printf "%s" .Site.BaseURL }}
{{ if eq $baseurl "http://estebanmoro.org/" }}
{{ with .Site.Params.googleAnalytics }}
{{ "<!-- Google Analytics -->" | safeHTML }}
<script>
var _gaq=[['_setAccount','{{ . }}'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
{{ end }}
{{ end }}
Note the if
before the script that tells Hugo to include it only if the base URL for the site coincides with the domain (in this case my domain http://estebanmoro.org)
). I have tried other solutions mentioned here and here, but this is the only that worked for me.
Math equations
One of the best thing (at least for me) of R markdown is the ability to type directly into \(\LaTeX\)
. This way I can type
$\int_0^\infty f(x) dx$
and it will be rendered as
\(\int_0^\infty f(x) dx\)
However, the casper-two
theme does not come with the MathJax javascript library which displays mathematical equations in the browser. To include it we have to add the javascript somewhere in the head of all pages. Let’s do it using the js.html
. Once again, edit the file in your layouts
folder and add the following line at the top:
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
Altmetrics
For those of us that work in academia, we use the number of citations or alternative ways to measure the impact of a paper. One of them is Altmetric which is particularly useful to track who is talking about your research in social media, newspapers, blogs, etc.
Here is how the Altmetric page looks for one of my papers:
On the top left you can see the Altmetric bagde for your paper, which is a sum of the mention in news outlets, blogs, Twitter users, etc.
Altmetric provides free javascript to embed that badge in your webpage (in a lab or personal homepage), so I decided to use it in each of my papers. Here are the instructions from the Altmetric page
- Firstly, we have to include the Altmetric javascript in our pages. Edit the
js.html
again to include:
<script id="altmetric-embed-js" type="text/javascript"
src='https://d1bxh8uas1mnw7.cloudfront.net/assets/embed.js'></script>
Note that I have included the id="altmetric-embed-js"
part to the script. The instructions at Altmetrics do not mention the id
.
-
Get the DOI for your article. The DOI is a universal identification number for each paper. Most likely you can get it from the journal publication page. For the my paper is
10.1371/journal.pone.0195750
-
If you want to include your badge in your R/markdown:
<div class='altmetric-embed' data-badge-type='donut'
data-badge-popover="right"
data-doi="10.1371/journal.pone.0195750"></div>
If everything worked you will see something like this:
You can modify and build your own badge. Check all the options here.
Custom CSS
You can also modify the style of your webpage through CSS. Although the CSS file of casper-two
theme is buried in the theme folder, there is a possibility to override some of the styles by using a custom CSS. To do that you have to edit the customCSS = [""]
in your config.toml
. In my case I added a file overrides.css
to the static/css
folder and change the config.toml
file to:
customCSS = ["css/overrides.css"]
Here are the contents of my overrides.css
in case you want to use it. It basically changes the title font size and weight:
.site-title {
font-weight: 200;
font-size: 7rem;
}
.site-header:before {
background: rgba(0, 0, 0, 0.5);
}
Show only some posts in the main page
On the things that I missed in my new blog is the ability to show in the main page only the posts that I want. For example, I have a list of publications that I update regularly but I don’t want it to show in the main page. Or you could have a tutorial and you only want it to show up in a specific part of your blog.
To do that we are going to add a new parameter (by post) hidden
that tells Hugo to show the post in the main page (hidden: false
) or not (hidden: true
). Edit your post and include it in the post YAML as it is done here:
---
title: List of Publications
author: Esteban Moro
date: '2019-01-31'
slug: list-of-publications
hidden: true
categories:
- Publications
image: "img/Publications.jpg"
---
Next thing is to modify the layouts of your theme so that when your blog is rendered by Hugo it does not show posts with hidden: true
in your main page.
The way I did it is the following:
- Copy the
post-list.hml
partial in thelayout/partials
folder of the theme to your ownlayout/partials
. - Rename the
post-list.html
topost-list-main.html
. The reason for this is that we are going to change the latter by we want the theme to use the former when necessary. - Edit the
post-list-main.html
it looks like this
{{ $paginator := .Paginate (where .Data.Pages.ByDate.Reverse "Type" "post") }}
{{ range $paginator.Pages }}
{{ if not .Params.hidden }}
<article class="post-card post">
{{if .Params.image}}
<a class="post-card-image-link" href="{{ .Permalink }}">
<div class="post-card-image" style="background-image: url({{.Params.image | absURL}})"></div>
</a>
{{else}}
<a class="post-card-image-link" href="{{ .Permalink }}">
<div class="post-card-image" style="background-image: url({{$.Site.BaseURL}}defimg/{{ index (seq 7 | shuffle) 0 }}.jpg)"></div>
</a>
{{end}}
<div class="post-card-content">
<a class="post-card-content-link" href="{{ .Permalink }}">
<header class="post-card-header">
{{if .Params.tags }}<span class="post-card-tags">{{ range $index, $tag := .Params.tags }}
#{{$tag}} {{end}} </span>
{{ end }}
<h2 class="post-card-title">{{.Title}}</h2>
</header>
<section class="post-card-excerpt">
{{ if .Description }}
<p>{{ .Description | markdownify }}</p>
{{else}}
<p>{{ .Summary | plainify | safeHTML }}{{ if .Truncated }} ... {{end}} </p>
{{end}}
</section>
</a>
<footer class="post-card-meta">
<img class="author-profile-image" src="{{ (.Params.authorAvatar | default .Site.Params.authorAvatar) | absURL}}" alt="Author" />
<span class="post-card-author"><a href="/">{{.Params.author | default .Site.Params.author}}</a></span>
</footer>
</div>
</article>
{{ end }}
{{ end }}
Note the if
in the third line that only renders each of the posts if .Params.hidden
is not true.
Finally we have to modify index.html
(once again copy it to your local layouts
folder) to tell Hugo to use the new post-list-main.html
instead of the old post-list.html
. If you edit index.html
change these lines:
<div class="post-feed">
{{ partial "post-list" . }}
</div>
to this one:
<div class="post-feed">
{{ partial "post-list-main" . }}
</div>
And you are done. All posts with hidden: true
in their YAML will not be shown in the main page. But they will appear in the categories, recommended posts, etc. If you don’t want them to show in those pages, edit the partials for each of them to include the {{ if not .Params.hidden }}
.
Fixing Youtube
Hugo has a number of shortcodes to embed tweets, videos, gist, etc. For example to embed a youtube video at https://www.youtube.com/watch?v=w7Ft2ymGmfc
you simply have to put this in your R/markdown:
However, this does not work with the casper-two
theme, an issue that has been reported in the theme homepage, but has not been fixed yet.
To fix it, you can use the solution proposed by Softinio in the opened issue page. It is very simple but requires to create a Hugo shortcode:
- Create a new file named
yt.hml
in yourlayouts/shortcodes
folder. - Put the following code in that file:
<iframe src="https://www.youtube.com/embed/{{ index .Params 0 }}?start={{ index .Params 1 }}"
style="position: absolute; top: 0; left: 0; width: 560; height: 315;"
allowfullscreen frameborder="0" title="YouTube Video"></iframe>
- You can now use the new shortcode in your R/markdown like this:
Summary
That’s it. It’s a long post (sorry), but I hope you might find it useful. Let me know if there is a better way to do these modifications! 🚀