Portfolio and Technology Blog
by Kevin Boyd

Sculpin Tips: HTML Snippets with Twig Macros

Sculpin is a PHP-based static website generator, ideal for powering websites that don't need dynamic DB-driven features or web-based administration. It's commonly used for blogging, but can be used for pretty much any marketing site you need, such as corporate websites, online portfolios, or even emergency downtime pages for when your WordPress site has been hacked ... (Kidding! Proper maintenance and security practices will make WordPress downtime a rarity.)

When running a website, you'll often want to reuse snippets of HTML. Many editors and IDEs have this functionality built in, but the problem with using editor snippets is maintainability. If you want to alter the snippet, you have to suss out every place it was used and manually edit it.

Sculpin uses the Twig templating engine, which has a feature that solves this problem. Twig macros are kind of like functions, like you would find in PHP or Javascript. They can be a bit tricky to get started with, so check out the tips below for help.

I recently wrote a really long blog post about a tech conference I attended, and I wanted to have a fancy snippet of HTML for speaker information. At the top of the post's markdown file, I defined a macro like this:

{% macro speaker_info(info) %}
    Speaker: {{ info.name }}
{% endmacro %}

Then I tried using it for one of my talk summaries (fictional example provided):

#### Extending Sculpin using Twig, PHP, and JavaScript
{{ speaker_info({name: 'Kevin Boyd'}) }}

**My takeaway:** Cool talk, Me. I learned a lot from me.

Spoiler alert: It didn't work. Thankfully, the solution was simple. (First Rule of Simple: It's never simple.)

After consulting the Twig documentation for the macro tag, I learned I had to import the macro from "_self" into a variable. I put the code for this directly underneath endmacro:

{% endmacro %}
{% import _self as conf %}

The variable conf can be whatever you want.

Next, make the speaker_info call use the variable:

#### Extending Sculpin using Twig, PHP, and JavaScript
{{ conf.speaker_info({name: 'Kevin Boyd'}) }}

**My takeaway:** Whoa.

Success! This is just a basic example, of course. I jazzed it up a bit for the actual post, including fancy buttons and optional inputs. I won't talk about buttons (they vary by which CSS framework you choose, or your own latent CSS talents), but it would probably be helpful to explain what {name: 'Kevin Boyd'} does, and how you can use it to provide optional input to your macros.

These structures are known as "dictionaries" in Python, but Twig calls them "hashes". You might also be familiar with the term "associative arrays" in PHP. Essentially, they let you organize data into simple structures that you can pass around. Here, we're using a hash to pass our data into the conf.speaker_info macro.

To pass more than just the speaker's name, we can add extra key/value pairs:

{{ conf.speaker_info({name: 'Kevin Boyd', github: 'beryllium'}) }}

Then we update our macro to react to this new input:

{% macro speaker_info(info) %}
    <p>Speaker: {{ info.name }}</p>
    <p><a href="https://github.com/{{ info.github }}">GitHub</a></p>
{% endmacro %}

Now, what if some speakers don't have a github account? While the natural reaction might be "ha ha ha, as if!", it's entirely reasonable not to have one. I know I've read at least one blog post that strongly suggests github-powered interview processes are flawed and biased, and that maintaining an account simply isn't feasible for some people. Food for thought. But, for the time being, it's also food for exploding our macro. Kaboom!

You see, programming languages don't like it when you reference things that don't exist - so if you pass speaker data into our macro without the GitHub username, it might complain, or it might fail silently (and that can be worse). So what are we to do?

Well, it turns out that Twig also has options for logic, such as if/else statements. It can also "pipe" data to other filters, which we can use to specify a default value for the GitHub username. We're going to use false, so the code knows that it should hide the GitHub link.

{% macro speaker_info(info) %}
    <p>Speaker: {{ info.name }}</p>
    {% if info.github|default(false) %}
        <p><a href="https://github.com/{{ info.github }}">GitHub</a></p>
    {% endif %}
{% endmacro %}

There you have it: a Twig macro you can embed in a Sculpin page or blog post. But how useful is a snippet you can only use on one part of your website?

Using macros in multiple places is a matter of putting them in a dedicated file and then importing them onto any page or post that needs to use them. I suggest doing this by creating a macro file called source/_includes/macros.twig. Then, in any page that needs to use them, call {% import 'macros.twig' as my_macros %} to make them accessible.

So far I haven't found an easy way to make the macros globally accessible by default, but these tips should be a good start for working with reusable HTML snippets.

Thanks for reading! Now back to your regularly scheduled programming.

Edited Oct 24, 2015: GitHub user thomas-p-wilson discovered that "source/_includes/", rather than "source/_views/", seems to be a place to store macro files for use in posts and pages.

Published: July 21, 2015

Categories: howto, coding

Tags: dev, development, coding, howto, twig, sculpin

comments powered by Disqus