Deconstructing the Defaults

The best way to learn is through example, right?
Let’s break down the Default Theme and examine it piece by piece.


Open this documentation on GitHub Found a typo? Something is wrong in this documentation? Just fork and edit it!

Pico’s Default Theme is intentionally simple and straightforward. You can consider it the “reference implementation” of Pico’s default functionality. A lot of the code you see here will be similar, if not identical, to what you’ll need for your own projects. The Default Theme itself can also act as a good starting template for building your own theme.

The best way to learn is through example, right? Let’s get started breaking down the Default Theme into bite sized code snippets. As we go, we’ll stop to explain all the Twig code and what it’s doing.

It’s all in your Head

There’s nothing overly complicated about the head section of your Pico page. The Twig bits mostly involve using simple logic to check if the user defined a few variables, then adding them to the HTML head.

Title and Description

<title>{% if meta.title %}{{ meta.title }} | {% endif %}{{ site_title }}</title>
{% if meta.description %}
    <meta name="description" content="{{ meta.description|striptags }}" />
{% endif %}

Here we have an HTML Title tag, providing the page title. In the example, we check if the page has a meta.title, then if so, we print the title followed by an I-bar separtor | and the site_title. With this setup, your page would look something like “My Page Title | My Website” in the browser’s tab or titlebar.

Next, if there’s a meta.description for the page, we add that to the HTML as well. In this case, we’ve run it through Twig’s striptags filter to remove any formatting the user may have provided. While we may want this formatting later when we’re building the page itself, remember, we’re still filling out the head! It’s not going to be happy with some extra tags jammed in there.

Some Statements on If Statements

Both of these examples check if a variable exists before printing the corresponding code. In Twig, you can check for the existance of any variable with simple if statements like these.

It’s a little more complicated than that, Twig’s just good at figuring out what should evaluate to true and what should evaluate to false. You can check the Twig Docs on If Statements for more information. Generally though, if something seems like it should be false, like null, 0, an empty array [], or of course false, it will be. Almost everything else evaluates as true.

Be warned though, this can sometimes get a little confusing. A variable that isn’t defined and a variable that is explicitly set to false will look identical to this simple if statement. If you ever need to know that something was intentionally set to false, you should also check if it is defined.

As you can see, unlike some languages, Twig won’t crash from trying to access a variable that doesn’t exist. It just returns null instead. In Pico, this is because Twig’s strict_variables option defaults to false. If you enable strict_variables in either your config.yml or pico-theme.yml, Twig will instead throw an error in these situations.

Robots

{% if meta.robots %}
    <meta name="robots" content="{{ meta.robots }}" />
{% endif %}

Does not compute? Don’t worry about it. This bit is to set up the HTML robots meta tag, which allows you to tell some search engines whether or not to crawl a particular page. Including this in your theme is nice touch (Pico even defines the Robot metadata header out-of-the-box), but its entirely optional. When in doubt, just copy this block as is, and it’ll be there for anyone that wants it.

Canonical URL

{% if current_page %}
    <link rel="canonical" href="{{ current_page.url }}" />
{% endif %}

Another bit for your friendly neighborhood webcrawler. The canonical tag tells search bots the proper URL to index your page as. This can help them to filter out things like query strings and redirected links that might otherwise confuse them. Again, this is optional, but it really doesn’t hurt to include it.

Stylesheets

<link rel="stylesheet" href="{{ theme_url }}/css/style.css" type="text/css" />
<link rel="stylesheet" href="{{ theme_url }}/css/droidsans.css" type="text/css" />
<link rel="stylesheet" href="{{ theme_url }}/css/fontello.css" type="text/css" />

You probably want to link some stylesheets or Javascript to your theme. The best way to do this is using theme_url to get the location of your specific theme’s folder. This will make sure those resourses always get found, no matter what your Pico installation looks like.

{% if pages["_meta"].meta.logo %}
    <div id="logo">
        <a href="{{ "index"|link }}">
            <img src="{{ pages["_meta"].meta.logo|url }}" alt="" />
        </a>
    </div>
{% endif %}

This one’s not too hard, but there’s a few different things going on. First, we check if the user has filled out the Logo: metadata property in their _meta.md page (if not, that’s it, we stop there).

Next, we’re making a link to the index page using the link filter. This will generate a proper link to the index page regardless of whether the user has [URL Rewriting][] enabled or not. It’s the recommended way to manually link to your content pages.

Finally, we attach an image using the meta.logo property from _meta.md as our source URL. We run it through the url filter to make sure any of Pico’s [URL variables][], like %base_url% and %assets_url% get replaced in the output.

Site Title and Tagline

<div id="title"{{ pages["_meta"].meta.tagline ? ' class="tagline"' }}>
    <a href="{{ "index"|link }}">
        <h1>{{ site_title }}</h1>
        {{ pages["_meta"].meta.tagline|markdown }}
    </a>
</div>

A Tangent about Ternaries

The first thing to look at in this example is the outer containing div tag. There’s an interesting code snippet we haven’t seen before here. This bit of code uses a Ternary Operator, which is like a condensed if else statement. It’s definitely more of an advanced syntax, but it can be really helpful in places where a traditional if else block of code would be cumbersome.

In our example, we’re asking “Does meta.tagline (on _meta.md) exist? If so, print ' class="tagline"' to the page.” So, if a user has defined the Tagline: meta property, this div will have an extra class on it that it wouldn’t otherwise have (allowing us to apply some extra styles in our CSS). In most cases, a Ternary statement is formatted condition ? response_if_true : response_if_false, though you’ll notice in the example, we aren’t actually doing anything if false.

For Page in Pages()

<div id="nav" role="navigation" tabindex="-1">
    <ul>
        {% for page in pages(depthOffset=-1) if not page.hidden %}
            <li{% if page.id == current_page.id %} class="active"{% endif %}>
                <a href="{{ page.url }}">{{ page.title ?: page.id }}</a>
            </li>
        {% endfor %}
    </ul>
</div>

“For Page in Pages()” has a nice rhyme to it, huh? Need to access your Pages? This simple for loop is usually how you’ll do it.

In our example, the pages() function is given depthOffset=-1 that way it’ll also return the index page in its results. So, we’re looping through each page that the pages() function returns, as long as that page isn’t hidden (meaning it has Hidden: true in its metadata).

Next, as we’re building our navigation list, we check if the page id we’re looking at matches the id of the current_page. If so, we add the active class to that list item for styling.

Finally, we link to the page’s url, using the page title for the link text if it exists. If there’s no title, we fall back to using the page’s id.

You’ll notice this looks similar to the [Ternary Operator][] we discussed above. This time, it’s a shorthand version which equates to “Is there a page.title? If so, use that. If not, use page.id instead.”

Page Content

<div id="main" role="main">
    <div class="container">
        {{ content }}
    </div>
</div>

What, you were expecting something hard? {{ content }} will print the Markdown processed content of the current page. Navigate to a different page, you’ll see different content. Easy as that.

<div class="social">
    {% for social in pages["_meta"].meta.social %}
        <a href="{{ social.url }}" title="{{ social.title }}" role="button">
            <span class="icon-{{ social.icon }}" aria-hidden="true"></span>
            <span class="sr-only">{{ social.title }}</span>
        </a>
    {% endfor %}
</div>

Let’s look at how the Default Theme handles social links. Like Logo and Tagline, these are extra metadata headers defined in pico-theme.yml.

Here we have another for loop, but this time we aren’t iterating over pages(). Instead, we’re looking at each entry within the social header. For each item, we’re making a new link and filling out the details with the item’s url, title, and icon. Nothing we haven’t seen before here.

The only thing special to note is that social.icon is only half the class name. The Default Theme uses [Fontello][] for icons, which uses the convention of starting every name with icon-. To make it simpler for the end user, the icon- part is hardcoded here and they’ll only have to provide the specific icon’s name. This is a fairly typical practice for web icons, [Font Awesome][] for example often starts theirs with fa-. While it’s certainly not a Pico specific thing, keeping the user experience in mind almost always results in better code!


Up Next…

Where to go from here. Two choices.

Twig Tips and Tricks Not More Examples

GitHub Pages - This page was generated from 48ae8bf8adbdaddd97baea3870469b369c421f98 at 2022-06-08 21:05:42 +0000

Pico was made by Gilbert Pellegrom and is maintained by The Pico Community. Released under the MIT license.