Some tweaks
Before moving onto the major topics, let’s improve Basic a bit more…
Better header partial
This changes a few things:
- Anytime you’re handling page variables such as .Title or .Description, it’s best to pass them through the
markdownify
andplainify
functions, which first converts any markdown into HTML and then strips the HTML tags. Only site variables like .Site.Title are skipped since it’s reasonable to assume they won’t use markdown or HTML in their value (but if you do, give them the same treatment).- E.g.
*example* (markdownify)-> <em>example</em> (plainify)-> example
. In places where it would be appropriate to have HTML tags, you can just use themarkdownify
function. - The
safeHTML
function is used to declare HTML unicode like…
as safe to insert, you can tell when the function is needed if you see&
in place of the&
symbol in unicodes (e.g.&hellip
).
- E.g.
<title>
will be prefixed with the page .Title if the page isn’t the homepage (e.g.First blog post | Basic
).- Two
<meta>
tags are included to define the page’s description and keywords. If the page doesn’t have these variables set, it uses the .Site.Params from the config as a fallback. - The header is a link for easier navigation, and is given
aria-label="Hero banner"
to give a name for screen readers to announce (and so it can be uniquely selected in CSS if needed).
This “better” header partial isn’t perfect either, and uses way too many
if
functions for the sake of simplicity, but, I’ll cover how to simplify logic like this in the future.
CSS styling
Here’s a basic stylesheet to make your website nicer to look at:
This stylesheet is solely for making differentiating neighbouring elements easier, and to keep things legible enough for demonstration purposes, feel free to come up with your own style.
More on layouts, and the range
function
The range
function
To begin, create the _default/section.html
layout that was previously left out:
Here’s what’s happening:
range .Pages
lists all pages underblog/
, and it also rebinds the context to each page it finds.- .RelPermalink is the relative URL of the page, use .Permalink if you want absolute URLs.
- .Title is the title of the page, and is being used as the link’s text as well as its tool-tip.
This list could become pretty long over time, so you can use conditional functions to only list the 10 most recent pages:
See the
where
function’s documentation for how to nest conditionals.
Smarter use of layouts
Now that you know what the range
function is, you can use it in the homepage to list the website’s sections and taxonomies:
Doing
where .Site.Pages "Kind" "in" (slice "taxonomy" "section")
essentially asks for .Site.Pages which are either a kind of “taxonomy” or a kind of “section”.
And, so you can stop seeing “Page Not Found” everywhere, add the files for taxonomies and terms too:
Except, you don’t actually need these files. Sections, taxonomies, terms, and even the homepage are actually “list” pages, meaning instead of a separate layout file for each, you can instead create layouts/_default/list.html
and reserve the other files for when you need to override the list layout.
So, since taxonomy.html
and term.html
have identical content, you can simplify this to:
And keep layouts/index.html
& layouts/_default/section.html
since they’re different.
Moreover, have you noticed the <a>
element is getting duplicated? Perhaps you should create a partial for it? Actually no, there’s an even better way:
And then, replace every instance of <a>
with .Render "li"
, for example:
The advantage is that you can define different list items for sections, taxonomies, or terms by creating layouts/name-of-list-page/li.html
, and then specifying it with .Render
(e.g. .Render "blog/li"
).
Or, you could just create
layouts/arbitrary-name.html
;.Render
doesn’t care about the filename.
Context rebinding issues
So far, I’ve only demonstrated very simple context rebind scenarios, such as .Title inside of range .Pages
, however, let me show you something more complicated to demonstrate how you can deal with trickier problems.
Let’s say you had something like this:
In this case, .Site.Title would be blank, since the .Pages themselves don’t have a site title. However, you can fix this by rebinding the context of .Site.Title to the root of the page the variable is declared in using $:
Now, this works as you’d expect. But, what if you moved $.Site.Title into a partial?
This won’t work because $ rebinds the context of .Site.Title to the root of the page the variable is declared in, however, the variable is declared in the partial, and the partial’s context is still inside range .Pages
. You could solve this with {{ partial "example.html" $ }}
, but, what if you wanted to access variables from inside range .Pages
at the same time?
Thankfully, you can instead take advantage of the page variable:
The page variable is set to the root of the page you’re currently viewing, hence why it isn’t called .page since it can’t have a different context (it’s the same reason the now variable has no dot), and it can be used to reliably escape any nesting of functions and partials possible.
If you find yourself using page.Site like in the above example, you can instead use the site variable (e.g. site.Title).