Introduction
This is the third blog/site I’ve built in the last few years. Knowing very little about web development, but wanting to develop some kind of curated web presence, I’ve moved from Blogger to Jekyll (hosted on GitHub) and now to Hugo (which I’m hoping to host on GitLab using a custom domain) via the blogdown
package in R. It’s really my comfort with R, Rstudio, and knitr
/rmarkdown
that has prompted this move, because I want to take advantage of all the features I’m used to in the R/RStudio universe of packages and products, and in particular the integration of html widgets that can be produced using .Rmd files.
But one thing I’ve learned from building my sites is that I need to take notes regarding what steps I’ve taken to get where I’m at. Otherwise I forget and then don’t know how my own site even works.
So, this post is mostly for me, as a record of how I setup my site and how to do things with blogdown and Hugo.
Reminder: How to make new posts
- RStudio -> Addins -> BLOGDOWN: New Post
- Brings up a nice dialog box for filling out typical information.
blogdown::new_post()
- This version of the workflow will require filling out some arguments to properly execute the function.
New post information
Both of the workflows above allow me to fill out some basic information about the post and how it should be integrated into the structure of the site.
- Title: Title of the post.
- Author: Author of the post.
- Date: Date of the post.
- Subdirectory: Where the post should be saved. Save to post for the sake of consistency.
- Categories: Metadata about a post to help people search for content.
- Tags: Similar to categories.
- Archetype: A file containing a “template” of what front matter should be populated when making content.
- It appears that the Hugo Future Imperfect Slim theme has two archetypes in
themes\hugo-future-imperfect-slim\archetypes
:blog.md
default.md
- It appears that the Hugo Future Imperfect Slim theme has two archetypes in
- Filename: Name of the file to be created. Automatically generated, but can be edited.
- Slug: A short title for the post, appended to the date. Ex “this-is-a-slug”
- Language: The language the post is in. My default is english.
- Format: .md vs .Rmd vs .Rmarkdown
Reminder: How to “preview” the site
Once I’ve made a post I can preview the site two ways:
- RStudio -> Addins -> BLOGDOWN: Serve Site
blogdown::serve_site()
This will activate the site in the Viewer tab. Usually it’s worth hitting the “Show in new window” button and viewing in an actual web browser.
Getting started
The main resource I’m using for setting up this site is the blogdown book written by Xie, Thomas, and Hill: https://bookdown.org/yihui/blogdown/.
Installation and theme selection
I’m using blogdown and RStudio to manage my Hugo site. Along those lines, the first thing I did was install blogdown and then used blogdown to install Hugo via blogdown::install_hugo()
.
In the future, if I want to update Hugo I can use the command blogdown::update_hugo
.
Since Hugo is it’s own program, it is worth to know that I can access Hugo’s command line with the command: blogdown::hugo_cmd()
. This is useful for some troubleshooting that sometimes needs to happen.
More about Hugo: https://gohugo.io/
Site creation and theme selection
To generate my site I used an R project: Open Rstudio -> click the projects drop down -> New Project… -> New Directory (I don’t think you can make a Hugo site in an existing directory) -> Website using blogdown
At this point a dialog popped-up with some additional information for me to fill out. Specifically, I included the directory name (wetlandscapes) and the location of the site on my computer.
Here, though I had to play around a bit and ended up making multiple sites as I developed understanding of both blogdown and Hugo.
First site
The first site I installed used the default options, including the Hugo theme from Yihui’s site, Lithium. This was a good first step because it let me see the folder structure of a Hugo site and how some of the different options and meta files (e.g., config.toml) changed the site.
Current site(s)
However, I didn’t ultimately like the Lithium theme. So, I went looking for some templates at: https://themes.gohugo.io/
After looking around a bit I came across the theme Hugo Future Imperfect Slim (HFIS), which had some nice built-in features, such as social media sharing, and icon links from Font Awesome places like GitLab, ORCiD, Research Gate, etc. This being an academic blog, I thought that was nice.
Initially I tried changing the theme of my first site using blogdown::install_theme()
, but I quickly became disoriented, as I didn’t really know what the `config.toml file was telling me.
To help me understand what was going on with the theme, I ended up building two more sites. Turns out when you build a site in blogdown you have the option of populating the site with sample blog posts and the example site from the theme. So what I did was I build a “skeleton” site with HFIS, where I populated nothing, and then I built the example site in another folder. This allowed me to see how the two sites differed and slowly build the site I wanted around my understanding of the example site.
Configuration
Global
config.toml
Like Jekyll, Hugo appears to use a config file for a lot of site functionality: config.toml
. My file has different “sections” and “subsections” enclosed in square brackets:
- (header): The top of the toml file seems to have some global options
- [params]
- mainSections: Seems to be the order in which pages are displayed on the home and
blog/
pages. For that reason I have “post” first, because I want my blog posts to display first. - [params.meta]
- [params.header]
- [params.intro]
- [params.intro.pic]
- [params.sidebar]
- [params.footer]
- mainSections: Seems to be the order in which pages are displayed on the home and
- [[menu.main]]
- name: The name of the page.
- identifier: Seems to be the name of the folder the content will go into.
- url: Relative path of the page. Note the “/page/” syntax.
- pre: A symbol representing the page, from Font Awesome.
- weight: I think this is display importance for when the display windo changes.
- [[menu.main]]
- [Languages]
- [Languages.en]
- [social]
blogdown
.Rprofile
There are a bunch options that can be modified. To ensure those options are preserved from session to session I need to make a .Rprofile file.
To setup the .Rprofile for the site I used the terminal in RStudio, which I’ve setup to used to Ubuntu for Windows Subsystem for Linux. List of commands I used:
touch .Rprofile
nano .Rprofile
#Once inside nano
options(blogdown.ext = ".Rmd",
blogdown.author = "Jason Mercer",
blogdown.subdir = "blog")
#Note the blank line -- R ignores the last line, so need make sure a trailing
# line is included.
#Exit nano and check that the edits occurred
cat .Rprofile
The reason I’ve set my default blogdown extension as .Rmd (as opposed to .md) is because, in the context of knitr and blogdown, .Rmd files are more flexible in their capabilities. For example, Table 1.2 of https://bookdown.org/yihui/blogdown/output-format.html illustrates that .Rmd files automatically include MathJax and HTML widgets, the later of which was severly lacking when I was using my Jekyll site (not that it is impossible with Jekyll, there’s just more tinkering on the backend that I don’t really want to deal with).
The reason the default subdirectory is blog/ is because the HFIS theme expects blog posts in that folder and not post/.
_output.yml
I can also add some standard output options by making and using a _output.yml
file. For example, I used the following code to get a standard output.
touch _output.yml
nano _output.yml
#Inside nano
blogdown::html_page:
out.width: '100%'
dev: 'svg'
Post
Hugo front matter (yaml/toml/json header information)
Front matter is the stuff that goes at the top of a post or other content. This is similar to Jekyll. Basically, the front matter is metadata about the post or content. For more about front matter: https://www.youtube.com/watch?v=Yh2xKRJGff4
Here is a list of front matter variables that Hugo is aware of: https://gohugo.io/content-management/front-matter/
Note: My site is setup to use yaml front matter variables. This was an option (check box) when I made the site: “Convert all metadata to YAML”. I didn’t know what this meant at the time, but apparently you can supply front matter to Hugo as yaml, toml, or json. I think by converting all metadata to yaml it makes the interface more consistent, but I don’t know that for sure.
Some useful predefined variables (that all Hugo sites know):
- date
- description
- images
- linktitle: I think this can be used as an alternative to the title, if the title is really long.
- slug
- summary
- title
- type: For blog posts on my site I always need to set this to “post” for the blog entry to register properly.
- weight
Some variables that I think are defined by my theme:
- author
- categories
- featured
- featuredpath
blogdown front matter
The yaml used in a typical knitr .Rmd file also applies to blogdown posts. For example, take the yaml header used to create this document. In this case the output
variable uses blogdown’s html_page, which can then take arguments, like toc
, that I’m used to dealing with from knitr.
---
title: Notes on setting up my new blogdown/Hugo site
author: Jason Mercer
date: '2019-11-08'
slug: notes-on-setting-up-my-new-blogdown-hugo-site
categories:
- meta
tags: []
description: ''
images:
- ''
linktitle: ''
output:
blogdown::html_page:
toc: true
---
A special note about the featured
and featuredpath
variables
There isn’t much documentation on these variables, so I had to play around with them for a while before I understood how to use them since there isn’t much documentation on how they work and Hugo’s code is still foreign to me.
featured
is the name of the image used as a thumbnail.featuredpath
is the location of the image. It is assumed that the image from featured is located in /static/img/blog
In some cases the assumption of featuredpath
is fine, but when I want to use images from the knitr output, I can’t use this path convention. Further, the featured image must be a raster graphic (e.g., jpeg, png). It was the combination of these two issues that was giving me serious headaches.
Below is my work around for using an image generated from my analysis as the “featured” image of the blog post:
Figure out which image I want to use. Name the knitr chunk something that can be used to identify the image a bit easier.
Use the
dev
argument to specify which kinds of images you want to use.It’s important that you specify the extions (e.g., .svg) that you want to show up in the actual blog first (e.g., svg is before png).
As an example, this is what the chunk options might look like:
r chunk_image, dev=c('svg', 'png'), eval=FALSE
The two image files will be named with the following pattern:
- . . From the example above, assuming their is only one image in the chunk, the two files will be called: - chunk_image-1.svg
- chunk_image-1.png
These files will be located in: `static/blog/
- - - _files/figure-html Given this pattern, and the assumption the
featurepath
variable makes, this means that we have to assign featured and feature path the following values to generate the right thumbnail for the post (expanding on our example):featured
: chunk_image-1.pngfeaturepath
: ../blog/2019-11-08-my-blog-post_files/figure-html
For the feature path, all one needs to do is remember
../blog/
and_files/figure-html
and combine that information with that of thedate
variable andslug
variable.You need to makes sure the
images
variable is removed. It seems this pathway (default: ’’) overrides the featuredpath.
Here is what the yaml metadata will look like for a minimally working example (R chunk with image is names “iris”):
---
date: '2019-11-09'
slug: hello-hugo-and-blogdown
featured: iris-1.png
featuredpath: ../blog/2019-11-09-hello-hugo-and-blogdown_files/figure-html/
type: post
---
When there are not featured images
I discovered that if I leave the images
variable as blank in the front matter it creates a “blank” image in Firefox. So, it seems like best practice to just delete all the variables that aren’t filled out, in order to avoid this kind of behavior. Below is an example of my updated header for this post.
---
title: Notes on setting up my new blogdown/Hugo site
author: Jason Mercer
date: '2019-11-08'
slug: notes-on-setting-up-my-new-blogdown-hugo-site
categories:
- meta
tags:
- hugo
- tutorial
type: post
output:
blogdown::html_page:
toc: true
---
Pages
About
For this page, I more or less just copied over the about page from my Jekyll blog. At first I had some problems with the images not displaying properly, but I found out that my custom html/css just needed some “div” tags around them.
<!-- This works -->
<div>
<figure-right>
<img src="/img/about/about-viking.jpg">
<figcaption>
Packrafting the Anaktuvuk River, North Slope, Alaska.
</figcaption>
</figure-right>
</div>
<!-- This doesn't work; my captions wondered off -->
<figure-right>
<img src="/img/about/about-viking.jpg">
<figcaption>
Packrafting the Anaktuvuk River, North Slope, Alaska.
</figcaption>
</figure-right>
Research
Using the lesson from the about page, translating this page from Jekyll to Hugo was smooth.
CV
For this page I’m trying to replicate some functionality developed by Noam Ross, who developed an R script for downloading and populating his CV from ORCiD. Pretty neat! His script is located here.
The first issue I ran into was automatically downloading my ORCiD information. It appears I need use OAuth with the ORCID API. To get that information I performed the folllowing steps:
Ran
rorcid::orcid_auth()
Logged into ORCiD and authorized the use of OAuth
library(rorcid) orcid_credentials <- orcid_auth()
I then copied the key into my
.Rprofile
fileoptions(blogdown.ext = ".Rmd", blogdown.author = "Jason Mercer", blogdown.subdir = "blog", orcid_token = "XXXXXXX...")
Then I added
.Rprofile
to my.gitignore
file so my information wouldn’t be exposed.
To test if this solution worked I restarted R (ctrl + shift + f10
) and re-ran the rorcid code for downloading data and it worked!
However, not all of the DOIs associated with my works seem to be registering with CrossRef (though the actual DOIs do properly link to the underlying material), so I’ll have to figure that out at some point.
It looks Noam also included some manual publication information. I may end up using that to fix my problem, but for the time being I’m ignoring the manual publication stuff.
The result of the script are 4 yaml files in the data/ directory:
- education.yaml
- employment.yaml
- papers.yaml
- papers_orcid.yaml
I had a bit of trouble with the education yaml, so I ended up modifying Noam’s code a bit. For example, I ended up hardcoding “(Excpected) 2020” for my graduation date, as Hugo didn’t know what to do with the empy file. I also added an “order” element to the education lists, to help with getting my education in the right order. These changes are refelected in both the script and the file layouts/cv/cv.html
(which I’ll talk about later).
The next big issue was figuring out how to put all that yaml information together as there wasn’t an obvious way to use it in the context of markdown. This took a bit and I had to learn about “sections” in Hugo.
Basically, I:
- Added a
cv/
directory tocontent/
. - In
cv/
I added an_index.md
file (a convention in Hugo) with a single line of YAML: the title (“Curriculum Vitae”). This file basically “initializes” the layout I want, which aggregates all the yaml I made earlier. - I then made the file
layouts/cv/cv.html
. This file is basically justthemes\hugo-future-imperfect-slim/layouts/_default/single.html
, except that I replace the “{{ .Content }}” code with some code from Noam. - The code I used come’s from Noam’s
layouts/section/vitae.html
. He had a lot of extra stuff in there, so I just used some of the information from the eduction, professional experience, and publications sections. It wasn’t crazy hard to figure out, but is definitely weird looking. - Last, to get the professional experience code to work, I had to borrow a partial from Noam’s site:
layouts/partials/work.html
. This file I did not edit, but put straight into my own layout folder. - Other than that I just made some aesthetic edits that matched my own theme a bit more, compared to Noam’s site.
About Hugo
Page strategy
There are basically two kinds of pages in Hugo:
- List pages
- Single pages
List pages
List pages are basically aggregator pages. For example, my home page is a list page, because it’s listing all the blog posts on the page (or some of them). The categories and tags pages are also list pages.
Single pages
Single pages are one off pages. So, for example, my about and research pages are sinlge pages. Also, all the individual blog posts are single pages.
Folder structure
The following folders and files make-up the core structure of Hugo:
- config.toml
- content/
- themes/
- static/
TOML
The primary building block of toml is the key/value pair. For example:
key = "value"
Boolean’s in toml are represented as “true” and “false”
This is super detailed, but the time standard used by toml can be found here.
Arrays can be passed to toml using brackets (and brackets in brackets for multi-dimensional arrays). It seems that arrays don’t need to be the same type. Example:
colors = ["red", "white", "blue"]
nested_colors = [["red", "fuscha", "pink"], ["blue", "cyan"]]
Tables
toml also includes tables/hash tables/dictionaries. They are encolsed by square brackets.
Types of tables:
- Table
- Inline table
- Array of tables
For example, a table (top line in the example below) with multiple keys and values might look like:
[table]
key1 = "value1"
key2 = "value2"
Related tables are generally given similar names that build on some theme. For example:
[table]
key = "value"
[something.else]
key = "another value"
[something.other]
key = "yet another"
[something.other.thing] # This is really part of an array of tables
color = "red"
shape = "round"
An inline table is a bit more compact:
name = { first = "Tom", last = "Preston-Werner" }
point = { x = 1, y = 2 }
animal = { type.name = "pug" }
That is equivalent to:
[name]
first = "Tom"
last = "Preston-Werner"
[point]
x = 1
y = 2
[animal]
type.name = "pug"
An array of tables looks like:
[[menu.main]]
name = "Home"
identifier = "home"
url = "/"
pre = "<i class='fa fa-home'></i>"
weight = 1
Note that the indexing above is not necessary, but does help visually parse the code. Array elements (what’s in the double brackets) are inserted in the order they are encountered. That is why the menu in the config.toml files is in the order it is in, to ensure those elements, in that order show up in the header of the website.
config.toml
toml: Tom’s Obvious, Minimal Language
All the elements wrapped with “[]” and “[[]]” are related to specific structural elements listed above. It seems that some of these tables are associated with standard behavior in Hugo. For example:
- baseURL
- menu
- paginate
For a full list of the standard key and table values, check out (note that values in parentheses are the defaults): https://gohugo.io/getting-started/configuration/
Based on my understanding of this stuff, and a recommendation in the blogdown book, I added the following code to my config.toml file:
ignoreFiles = ["\\.Rmd$", "\\.Rmarkdown$", "_files$", "_cache$"]
[permalinks]
post = "/:year/:month/:day/:slug/"
Site setup
My expected site structure: * content/ * about.md * blog/ * … * curriculum-vitae/ * research.md * data/ * .yaml (related to the curriculum-vitae section) r/ * layouts/ * curriculum-vitae/ * partials/ * public/ * scripts/ * get-orcid-data.R * static/ * blog/ * … * img/ * about/ * blog/ * main/ * research/ * themes/
content/
This contains the main content for the site. Specifically, this is where the blog posts (html), and their underlying .Rmd/.md/.Rmarkdown files will live. So, when I make a new post in RStudio (Addins -> BLOGDOWN -> New Post), I’ve configured blogdown to put the files in content/blog/
, as is expected by the theme.
Most everything in this folder uses the “single” template (in the themes folder: layouts/_default/single.html
), which means they are formatted a specific way. The one exception to this are the files in the curriculum-vitae
folder, which is it’s own section, according to how Hugo sets up its file tree. In this respect, I’m controlling the look of the CURRICULUM VITAE page by including a special layouts html that corresponds the the _index.md
in content/curriculum-vitae/
. Basically, the index file acts as an indicator that I want a special section dedicated to my CV. I had to do things this way to get my links to work properly, otherwise I had this weird link set of ./cv/curriculum-vitae.html
, which isn’t terrible, but not as clean as ./cv/
. I probably could have made this less complicated, but this is what I came up with based on my limited understanding of Hugo.
The files about.md
and research.md
get processed like normal “single” pages, and have their own links due to the way Hugo processes files like this in the content folder.
data/
This folder is intended to host data that the site can use. I’m using if for two purposes.
- Hosting site data. Specifically the yaml files I use to build my CV from ORCiD.
- Hosting R data for blog posts that take a long time to build. Basically, I’m using it as a cache. I could probably avoid this kind of thing by better figuring out blogdown and knitr’s caching behavior, but one step at a time.
For the second purpose, I’ve made the folder data/r/
. I’ve also added "\\.Rdata$"
to the array of the ignoreFiles
variable in config.toml
.
I also found out that other data types (like a tiffs) are not respected by Hugo (they will cause Hugo to throw and error). To get around this, in the context of the data file, I added the following to the ignoreFiles
array: "data/r/.*". Now it ignores everyhing in the that folder. Pretty neat.
layouts/
This folder contains files that control the look of the site. Specifically, I’m using this folder to:
- Control the look of special sections of the site. Specifically, the page with my CV.
- Add files not included in the theme layouts folder. In particular Noam Ross’s
work.html
partial. - Provide alternative files to the theme layout that take precedence over the theme layout. I’m not actually using this functionality at the moment, but I could in the future.
public/
This is where Hugo builds the actual site. I don’t really need to do anything here.
scripts/
Right now this is just hosting a single file: get-orcid-data.R
. This file is used to download my ORCiD information for the CV page. I have to run this script manually, though I may be able to automate it with a different continuous integration setup on GitLab. Something to think about.
I’m not sure if I needed to, but I added "\\.R$"
to the ignoreFiles
array in the config file.
static/
This is where static files live. All contents in this file will be copied to public/ for use with the site.
The blog/
folder is built by blogdown and contains all the supplemental files (mostly images) used by the blog posts. I should not have to interact with this folder much, except when I want to set a featured image.
The img/
folder contains images I’m using for specific pages, sections, and blog posts. Note that the main/
folder is for the home page.
themes/
This folder contains all the aesthetic information (except for what is in my layouts/
folder) and code used to build the website. I’m not going to try to get very deep into this stuff until I actually want to start customizing my site, though I did have to get a little into it with the CV page.
About my theme
I was having a hard time getting the right bit of text to display in a header. I finally figured out that there was a partial I needed to update. Specifically in the theme that prioritizes section titles over page titles. I was able to reverse that priority. The correct bit of code, in layouts/partials/site-header.html
takes precedence over what is in the theme directory, so I should be okay, even if I update the theme later.
Turns out the above solution didn’t work; I ended up reverting back to the original partial file. Instead, I changed all section references from “cv” to “curriculum-vitae” in the file tree:
- layouts/curriculum-vitae/curriculum-vitae.html
- content/curriculum-vitae/
It seems that the site-header.html
partial also parses the name of the section from the file tree, so the “-” is not included and everything gets capitalized. I’m still not sure where this logic gets implemented, but it seems to work, so there you go.
Font awesome
I was able to update the fonts associated with the different sections in the config.toml
file. That is, each section (in [menu]
-> [[menu.main]]
) has a variable pre
that is associated with some fonts. I can change those fonts. W3Schools has a huge visual list of all the free Font Awesome fonts I can use.
Tweaks
Blog titles
For some reason the behavior of blog titles was set to break words, rather than wrap them to the next line, like normal. I was able to find the css associated with this choice deep in main.scss
. That is, it was in .post -> header -> word-break, which I changed from break-all
to normal
.
Also, it turns out I need to re-render the site everytime I make a change to the css otherwise the changes will not be updated in the public file, which is what I’m really viewing when the site is being served to my local host.
Links and references
Relative links
Hugo code
As far as I can tell, there are a couple of ways of getting relative links to work in blogdown/Hugo. The most verbose, but closest tied to Hugo itself (which should mean it always works) is to call the html itself via htmltools::HTML
. For example, to link this post I would use:
[this post](`r htmltools::HTML('{{< ref "/blog/2019-11-08-notes-on-setting-up-my-new-blogdown-hugo-site" >}}')`)
This results in: this post. Also not that the set of quotes needs to be in the order provided in the chunk above. That is, ’ needs to wrap the Hugo reference and " needs to wrap the blog reference.
Blogdown code
But that looks complicated. Can I simplify the code? Maybe. Here is some code that is little less verbose:
[this post](/blog/notes-on-setting-up-my-new-blogdown-hugo-site)
And this is how it will render: this post.
Note: This code works, at least, on the GitLab site, so I shouldn’t need to use the super verbose form of the code.
Tips and tricks
If you are working on a .Rmd file, but don’t want blogdown to render it, temporarily change the extension (e.g., .Rmdt) to something blogdown will not recognize.
If you don’t want a .Rmd (or equivalent) file to be rendered, you can set draft: true
in the yaml metadata. This will make the file available for local preview, but not part of the site build.
You can use “shortcode” as a quick way to write code. Here is an example of using shortcode in blogdown:
For more on Hugo shortcode: https://gohugo.io/content-management/shortcodes/
Useful resources
- The blogdown book
- Mike Dane’s (formerly Giraffe Academy) Hugo tutorial
- Output types supported by blogdown
- blogdown’s html_page is based on
bookdown::html_document2
, so see the help page for that function for more about that output type.
- blogdown’s html_page is based on
- TOML syntax
Example websites:
- Maëlle Salmon
- Noam Ross
- Emil Hvitfeldt
- Yihui’s static site:
- Another theme that looks cool for documentation: https://learn.netlify.com/en/
Things I’m still trying to figure out
- ~~What is going on with the config.toml file? Are these options universal (determined by Hugo), or determined by the theme? Which are which?
- A: https://gohugo.io/getting-started/configuration/~~
- Solution: A lot of options are theme-specific in the sense that the data associated with variables is accessed by code in the theme. Basically, there is a lot of flexibility built into Hugo.
- ~~What about the yaml header at the beginning of every post?
- Some seem to be predefined by Hugo and some seem to be defined by the theme.
- I’m not sure where the theme-specific variables are defined, so I can figure out what my options are and what those options mean.~~
What is the difference between post and blog?- Solution: For the theme I’m using, posts go in the blog folder, but are still call posts. For that reason, the front matter of all blog posts must include the line
type: post
for the theme to work properly.
- Solution: For the theme I’m using, posts go in the blog folder, but are still call posts. For that reason, the front matter of all blog posts must include the line
How can I add a picture from my .Rmd file as the “featured” image without having to specify the location or re-rendering the file?- How can I add Twitter activity to my site?
- This seems promising: https://gohugo.io/templates/internal/#twitter-cards
- Custom domain on GitLab
- ~~How do can I automatically download and format my publications from ORCiD?
- Noam Ross has something on this: https://www.noamross.net/2019/08/09/a-new-website/
- Source code: https://github.com/noamross/noamross.net
- Specifically, https://github.com/ropensci/rorcid~~
- Noam Ross has something on this: https://www.noamross.net/2019/08/09/a-new-website/
- How can I add a search bar?