Migrating to Zurb Panini 2.0
Not yet stable yet, but has some compelling features.
Why upgrade to 2.0?
I've been using Zurb's Panini static site generating template to power this site. On the whole I've generally found it quite good, but there's been a couple rough edges that continued to paper cut when creating content (even though I realise Panini wasn't made with blogging in mind).
One thorny limitation is there's no seamless way to generate a blog index or RSS feed. So in versions 1.X, I had to create and manually maintain a YML file with each blog entry.
Not the end of the world, but it's a copy paste job that would occasionally lead to mistakes and was tedious; an excerpt of my old manually rolled data/blog.yml
file:
- title: "Actioning web strategy & IA: The Content-Action Method for Web Systems"
layout: layouts/post.njk
teaser: "A method to distill content and user, organisational goals into an interconnected, holistic practice."
image: "/blog/drill-bits.jpg"
date: 2018-01-22
url: /posts/20180122-content-action-model.html
And beyond being tedious to create and maintain (copy-pasting each page's meta data), the method only gave me access to what's in the YML file — so that meant my RSS feed couldn't have the full blog posts:
\{{#each blog}}
<item>
<title>\{{this.title}}</title>
<description>\{{this.teaser}}</description>
<link>\{{root}}\{{this.url}}</link>
<pubDate>\{{this.date}}</pubDate>
</item>
\{{/each blog}}
But, lo! In version 2.0, a new \
variable.
\{{#each-reverse pages}}
\{{#ifequal this.[2].active_path "blog"}}
<item>
<title>\{{this.[2].title}}</title>
<description>
<![CDATA[
{{#markdown}}
{{#compileBlogPostForRSS this}}\{{{ ../this }}}\{{/compileBlogPostForRSS}}
{{/markdown}}
]]>
</description>
<link>\{{root}}\{{this.[2].url}}</link>
<guid>\{{root}}\{{this.[2].url}}</guid>
<pubDate>\{{this.[2].date}}</pubDate>
</item>
\{{/ifequal}}
\{{/each-reverse}}
The magic happens in my custom {{#compileBlogPostForRSS}}
helper, where I pass in the full body of each post and render it as a Handlebars template. Here's a link to that helper.
The approach is slightly messy as I must reach into the second [2]
spot of each page array, but it does the job. (n.b. This is meant to get better by changing from an arraay to named object.)
I now use the same approach to build this site's Blog index.
Upgrading from 1.x to 2.0
It's not so bad.
Remember Panini 2.0 isn't finalised yet, but as Zurb points out:
It's pretty stable, and if you want to give it a try, we'd love some feedback or bug reports on anything you run into.
Panini 1.X defaulted to use Gulp, and it was what worked best for me. So this guide is from gulp to gulp.
- Change your
packages.json
to use 2.0:"panini": "^2.0.0-alpha.2"
(Previously I was using Panini 1.5.1) - In your
gulpfile.js
dropvar panini = require('panini');
and addconst panini = require('panini/gulp');
- Panini now has default expectations about the naming of layouts, partials, etc. So my Gulp Panini task is now just (diff):
gulp.task('panini', () => {
return panini('src', {
// builtins: false,
})
.pipe(gulp.dest('build'));
});
- If you're using browserlab,
panini.refresh();
is a bit harder to use and is going away. Instead mygulp.watch
task now invokespanini.create();
. That may not be the most efficient way, but it works. #ifequal
is no longer a built in helper, instead it'seq
. But I didn't want to find-replace, so I just added the ifequal helper back.
- Apparently
#ifPage
is also out, but it still seems to be working. - Here's a short list of documented breaking changes.
Misc changes
I had to do a few other things that were unique to my setup.
- I was generating my critical path CSS as a partial called
critical.min.css.html
(🚨hack alert). It doesn't seem naming with dots are allowed in partials anymore, so I've called the filecriticalmincss.html
- I also had a page called
rss.xml
that was ignored by Panini. So I named itrss.html
and move the output torss.xml
. THis is meant be changing with the addition of a/static
build source.
- Previously I didn't have my
layouts,partials,data,pages
under a directory. I moved them to./src
.
Here's the commit from my upgrade to 2.0.
Other neat things
I've not yet used these yet, but they look like they'll really extend how long I can use Panini:
- Localisation and translation
- Inject content into blocks A bit like inverse partials, you can inject from a local page back into the parent template.
- The Handlebars Helpers project is now included, and it brings many helpful ways of sorting and conditionallys showing data, and Moment.js style date handling:
{{moment this.date format="YYYY MMM DD"}}
.
Panini still feels like a bit of unusual static templating engine. It's less feature complete than some, yet it also feels more light and tool-like than others. But I've enjoyed using it and it's been helpful, so thanks Zurb and contributors!
N.b. I don't think there's an existing Panini sample/quick-start blog project, if there's interest I could convert this site into a "Panini blog boilerplate".