New blog, new website, so here we go. I’ll start by describing how i
built the website. From scratch. The site is built with
zurb foundation , because i had read that it did everything that
hugo could, but that final products were more lightweight and
flexible. Plus i had no idea about it, and learning something new is
always often sometimes worthwhile. I was also frustrated that
standard
hugo advice seemed to be, ‘’oh, just pick a template and off you
go,’’ yet there is surprisingly little advice on how to modify any given
template, let alone how to start from scratch. It turned out that
foundation at least made starting from scratch fairly easy, and so
this entry is about that process. Note that i consider myself a
technically-oriented, back-end programmer more focussed on getting stuff
in and processing it than on getting stuff out. So when i say ‘’starting
from scratch,’’ i mean that most sincerely.
This is largely html
-related ramblings, so if you’re interested in the
code stuff, you might like to skip straight ahead to the next
section.
zurb provides a template (see
here for details) which deposits a basic infrastructure on your
local playground, along with the required
foundation libraries. The basic system is fairly well
documented , so
there’s little point going into that here. The top of this site is a
standard
top bar , and most of the rest is built from standard
callout containers or plain cells. This and all blog pages, for
example, are full-width
xy-grid containers with simple headers of
<div class="grid-x grid-padding-x">
<div class="cell medium-12 large-12">
The entire site lives within the local src/
directory, with the
remainder being stuff used by
foundation to build the site. This src
directory really is
impressively lightweight.
The primary components of
foundation are ‘’pages’’ and ‘’partials,’’ with the latter
identical to most other systems for building websites. Crudely
interpreted, ‘’pages’’ hold the actual content, while ‘’partials’’
define the styles, generally as html
header and footer components
inserted before and after the content of a page.
foundation integrates directly with arbitrarily-structured yaml
files, which made auto-generation of my main web page particularly easy.
The files themselves live in the src/data
directory, with the blog
entries, for example, read straight from a src/data/blog.yaml
file
that looks like this:
-
title: how i made this site
description: < blah blah blah >
created: 06 May 19
modified: 06 May 19
link: blog/blog001.html
-
title: C++ templates and Rcpp
description: < blah blah blah >
created: 07 May 19
modified: 07 May 19
link: blog/blog002.html
More on how that gets automatically generated below; for now, just
pretend it’s a static file. This has two entries, each of which has a
variety of components (such as title
, description
, and link
). The
‘’blog’’ section on the main page is generated directly from these
yaml
meta-data, using the {{#each blog}} command to automatically
loop over each of the above entries in the data/blog.yml
file, using
the same double-curly-bracket syntax from zurb’s
panini to insert variables into the html
code:
{{#each blog}}
{{> blog_header}}
<a href={{ link }}, style="color:#262626;">
<div align="center">
<h3>{{ title }}</h3>
</div>
<div align="center">
<p>{{ description }}</p>
</div>
</a>
{{> blog_footer}}
{{/each blog}}
The whole site is set up with a grid 12 squares across, so these are
full-width containers with grid-padding-x
, which by default reads
values from the global /src/assets/scss/_settings.scss
file. Yep, it’s
an
scss file, which is both great and … not so great. It means that
almost all variables used to generate your site - this site - can be
modified through directly modifying the values in
src/assets/scss/_settings.scss
. The not so great is that these are
global variables which are translated during compilation into css
variables which generally won’t share the same names. So if you want to
change these values locally rather than globally, you can’t ‘just do
it’, you are forced to revert to standard css
(to define class
structures) or html
(to explicitly define elements). This blog page,
for example, is defined by a simple entry in src/assets/scss/app.scss
– the sole location needed to define all local classes - as:
.blogClass{
margin-top: 0px;
margin-left: 50px;
margin-right: 50px;
}
The margin-
elements are bog-standard css
, and absent these custom
definitions all inherit the global properties specified in
src/assets/scss/_settings.scss
(definining standard properties of
foundation’s xy-grid
):
$grid-margin-gutters: (
small: 20px,
medium: 30px
);
$grid-padding-gutters: $grid-margin-gutters;
$grid-container-padding: $grid-padding-gutters;
Examples of html
modifications to the global default scss
variables
are the background colours for each component of the code and blog
sections. Remember that everything on the main page is a ‘callout’,
meaning that they all inherit the global variables defined in
src/assets/scss/_settings.scss
. I defined the global background as
$callout-backgroud: transparent;
so the background image would appear underneath everything by default.
This required local changes to render the components semi-transparent
white, which was achieved with a simple two-line
src/partials/blog_header.html
of:
<div class="large-4 medium-6 cell" style="background-color:#ffffffaa">
The code above with {{> blog_header }} simply inserts that header in its rightful place.
That is the very short version of how i got this site to look the way it does. It’s simple, but it was fairly easy, and most important to me was that i didn’t have to borrow somebody else’s arbitrary and way-more-difficult-to-modify-than-i-thought template for whatever other site/blog-generating system i may otherwise have chosen.
The steps roughly described above yielded a static site largely as you
see here. The only remaining step was automating the procedure of
updating the site. Perhaps the easiest approach would be to do this
manually, but as most of the content is contained within yaml
files,
this is a procedure ripe for automation. As the end product of most of
my coding efforts is packaged in R-form, i opted to automate this
procedure within R, although the same principles apply to any other
language. What this section effectively describes is how easy
foundation made the task of effectively recreating
Yihui Xie ’s fabulous blogdown package.
Subjective judgement here, but the blogdown package was first released
to cran in August 2017, and a lot has changed in that short time. As
often happens, the enormity of the task Yihui achieved with that package
can now be recreated in
foundation form much easier, and with much less code. In the case
of this site, it effectively amounts to connecting some kind of
blog_render()
function to a simple update of a yaml
text file, with
a few more tricks for other included elements, notably graphics.
With the help of partials, the entire html
formatting of a blog page
is as simple as a header with these few lines:
{{> header}}
<div class="blogClass">
<div class="grid-x grid-padding-x">
<div class="cell medium-12 large-12">
{{#markdown}}
and a footer simply closing each section with:
{{/markdown}}
</div>
</div>
</div>
(plus just a couple of extra lines to add the navigation bar at the side
– shown
here
in a navbar()
function, if you’re interested). In between is
‘’standard’’ markdown (at least in a form I’ve yet to encounter any
particular idosynracies with ..), which
foundation interprets seamlessly. Converting an Rmarkdown
(.Rmd
) document to a blog entry is thus in essence as simple as
rendering (via rmarkdown::render()
) it to some kind of standard
markdown, renaming that to .html
, and inserting the 5 lines of header
and four lines of footer shown above. The following function forms the
basis of a blog_render()
function:
blog_render <- function (fname) {
rmarkdown::render(paste0 (fname, ".Rmd"), rmarkdown::md_document(variant='gfm'))
file.rename (paste0 (fname, ".md"), paste0 (fname, ".html"))
conn <- file (paste0 (fname, ".html"))
md <- readLines (conn)
header <- c (<... defined above ...>)
footer <- c (<... defined above ...>)
md <- c (header, md, footer)
writeLines (md, conn)
close (conn)
}
Simply calling blog_render ("this_page")
will then render and
transform this_page.Rmd
into this_page.html
formatted for this
website. The full function used to generate these pages has a couple of
other sub-functions, mostly to move images to locations accessible by
foundation , and to replace a few character fields not otherwise
interpretable in either standard html
or
foundation terms. Examples of the latter are the {{ breadcrumbs }}
used by
foundation’s panini interpreter, which are replaced by corresponding
html
encodings; or the Rmarkdown code chunk delimiter, ```{r},
from which the curly brackets must be removed to replace it with, ```
r.
While it is possible to specify an image directory in the yaml
front-matter of an .Rmd
document, it was just as easy, and more
explict, to add another function to my blog_render()
function to move
them to the appropriate place in the
foundation directory, which is assets/img
, and any arbitrary
sub-directories thereof. The following lines achieve this
path <- file.path (paste0 (fname, "_files"), "figure-gfm")
flist <- list.files (path, full.names = TRUE)
newpath <- file.path ("..", "..", "assets", "img", fname)
if (!dir.exists (newpath))
dir.create (newpath, recursive = TRUE)
file.rename (flist, file.path (newpath, list.files (path)))
unlink (paste0 (fname, "_files"), recursive = TRUE)
along with a simple replacement in the main file of the former with the
latter path. A final parameter called center_images
, when TRUE
,
inserts simple <center>
and </center>
lines before and after the
standard markdown image insertion command (![](<path>/<to>/<image>)
).
The blog_render()
function then worked, but I still needed to
automatically update the front page to link directly to the latest
entry. Another fairly straightforward yaml
-processing task, this time
stripping the yaml
headers from all blog entries. This became the
second, and only other, main function, update_main()
. This function
essentially just strips the yaml
header data out of each .Rmd
blog
entry, and re-formats it slightly as the data/blog.yml
file. This in
turn relies on one main function, get_one_blog_dat()
which, for
example, converts the metadata for this entry of:
---
title: how i made this site
description: <blah blah blah>
date: 06/05/2019
link: blog/blog001.html
---
into the only slightly-modified version in data/blog.yml
of
-
title: how i made this site
description: <blah blah blah>
created: 06 May 19
modified: 06 May 19
link: blog/blog001.html
The “created” date is read from the original date
field of the .Rmd
metadata, while the “modified” date is the actual date of file
modification. These two dates enable blog entries to be sorted by dates
of either creation or modification with a simple binary parameter.
That’s it. It took me a little while to construct this site, but most of
the time was me learning how
zurb foundation works. Most of the mechanics of site construction
and updating are nevertheless done via the R code, which is really
very short and efficient. If you’re interested, the two files that do
the work are here, for rendering a blog
entry
and here, for updating the main
page.
The render_blog()
function calls the main updating function anyway, so
all I ever need to do is to call one simple function to render any new
blog entry and update the website.
The site itself is housed on the master
branch of
mpadge.github.io, while
the generating code behind the site is on the source
branch.
Deployment is controlled with a very simple bash
script,
called by a single makefile
command,
which builds the foundation site, copies everything across from the
source
to master
branches, adds the changes to git
, and creates a
commit to update the site. That’s it.
Advantages of having done this my own way:
Copyright © 2019--22 mark padgham