Build a static site in Ruby with Bridgetown

Once upon a time, in ye olden days of 2008, the world saw the release of Jekyll, the first popular static site generator. Fast forward 2+ decades, and its popularity has been eclipsed by newer JavaScript counterparts like Gatsby and Eleventy. And why not? Jekyll runs on Ruby (boo!) so it is unsexy and obviously super slow. (Hint: in that article, read down to where the author notes, "Also surprising is that Jekyll performed faster than Eleventy for every run.")

Sarcasm aside, Jekyll seems to be built on a solid enough foundation, but unfortunately it has not received a lot of updates in recent years. Is this another arena where, as they say, Ruby is dead?

Enter Bridgetown, a fork of Jekyll which aims to compete toe-to-toe with its modern JS cousins, providing even more Ruby tools for building static sites. Very exciting.

Many of Bridgetown's Ruby upgrades are already released, so I (happy for any chance to write Ruby) rebuilt and extended my blog with Bridgetown. Here's how I did it. Note that these instructions apply to Bridgetown 0.21, the latest version at the time of writing. Also note that a knowledge of Ruby is assumed here, but not necessarily any prior experience in building a static site.

You can see the final result of this process in my site's GitHub repo. The site itself is at fpsvogel.com.

1. Setup

  1. Follow the steps on Bridgetown's Getting Started page.
    • If you want to use a CSS framework: Bulmatown or Bootstrap blog theme. But instead I used a classless CSS framework (see below in "Design").
    • If you see strange or missing styling when serving up your site locally, just wait a bit when running yarn start until the Webpack manifest is generated (you'll be notified in the console when it is generated), then exit and run bridgetown serve. Note that bridgetown serve does not generate a Webpack manifest, so use yarn start or yarn webpack-dev whenever you see CSS issues and need to regenerate it.
  2. Install plugins:
  3. Switch to the resource content engine.
    • Doc: Resources
    • In bridgetown.config.yml, add content_engine: resource.
    • In the Liquid templates, change page to resource, and access front matter via the data variable. For example: instead of page.title, use resource.data.title.
  4. Switch from Liquid to ERB templating.
    • Doc: ERB and Beyond
    • In bridgetown.config.yml, add template_engine: erb.
    • Convert the Liquid templates in src/_layouts and src/_components to ERB. Then move the templates from _components into a new src/_partials directory, and add an underscore to the beginning of each filename, e.g. head.liquid becomes _head.erb.
    • Convert the Liquid code in posts.md to ERB.
  5. Set your preferred permalink style.
  6. Set up pagination.
  7. Create a _posts-drafts/ directory for drafts that will not be published with your other posts. (The documented way of doing this works only for the legacy content engine, but the docs will be updated by the time the resource content engine becomes the default.)
  8. Set your site's info in src/_data/site_metadata.yml.
  9. Add a Pygments CSS theme for code syntax highlighting.

2. Design

As a foundation I used holiday.css, a classless stylesheet for semantic HTML, and then I extended it with custom elements.

However, I still used CSS classes wherever a custom element would need to inherit from an element other than span or div, because this inheritance can only be set up via JavaScript, and that seems like more trouble than it's worth at this point.

Occasionally I took a part of a page and abstracted it out into a partial, such as _page_selector.erb and _tweet_button.erb.

3. Ruby component and plugin

Partials work great for simply sectioning off parts of the page, but for any significant manipulation of data prior to rendering, building a Ruby component makes more sense. In my case, I wanted a "Reading" page that lists titles from my reading.csv file (my homegrown alternative to Goodreads), including only books that I rated at least a 4 out of 5.

Following the doc on Ruby components, I created a ReadingList component in _components/reading_list.rb, and its template _components/reading_list.erb.

After writing the HTML + ERB + CSS for the reading list element, I used Stimulus to add JavaScript sprinkles to expand/collapse rows with reading notes or long blurbs, to filter rows by rating or genre, and to sort rows (though sorting is disabled on my site, since I went with the minimal "favorite or not" way of showing ratings).

Then I filled out reading_list.rb to load my reading list and provide data for reading_list.erb. This was just a matter of extracting CSV-parsing code from a previous app into a gem, then including it in my component and tying up the loose ends.

Thanks to a tip (one of many) from the Bridgetown creators on the Discord server, I realized my Ruby component had way too much logic in it which should be separated out into a plugin. So I dove into the docs on plugins and moved nearly all of my component's code into a plugin.

So now the plugin parses my CSV file and saves it into the site's data, then my component's .rb file pulls that data into instance variables, then the ERB template uses the instance variables as it displays the reading list.

If you create a plugin and want to make it more easily available to other Bridgetown site creators, you should make it into a gem and possibly create an automation for it. I didn't for my reading list plugin, because the intersection of people who track their reading in a CSV file and people who will make a Bridgetown site is… very few people, I'm sure.

4. Deployment and beyond

One possible improvement remains. Currently, to update the reading list I must delete it (_data/reading.yml) and rebuild the site locally (so that my reading.csv can be re-parsed) before pushing it to be built and deployed on Netlify. I could avoid these manual steps by taking advantage of the fact that my reading.csv is automatically synced to Dropbox: I could change my plugin to connect to Dropbox and update the list from there instead of from the copy on my local machine.

Conclusion

Besides Bridgetown itself, I learned a number of new things in this project:

  • Semantic HTML.
  • More CSS.
  • Stimulus, and more JavaScript than I'd written before.

But what I really loved was using what I already knew (Ruby) in a completely new way. Bridgetown is doing a wonderful job of bringing the joy of Ruby into the world of modern static site generators.

21