Skip to content

Tour of a theme

Daniel Pimley edited this page Nov 24, 2016 · 19 revisions

This article will take a tour of a Chyrp theme, to help you understand the things you'll need to know before developing a theme of your own or customising an existing theme. For this article, we will be taking a tour of Blossom, the default theme for Chyrp Lite, however many of the elements we will encounter are common to all Chyrp themes.

Twigs

If you're making a theme, twigs are what it's all about. Twigs are text files filled mostly with HTML but also with some instructions for Chyrp to follow, so that the twig can tell Chyrp what to do with it. When someone visits your website, Chyrp will build a web page by combining twigs together in various ways.

You can think of a twig as a fragment of a web page, just like a twig in real life is a fragment of a tree. The final product of every twig will be a piece of the page to be served to the visitor. When you view a twig, bear that in mind: nothing but the HTML will remain in the final web page served to the visitor.

The Tour

Download the latest release of Chyrp Lite and expand the zip archive. Now take a look inside the expanded chyrp-lite/themes/blossom/ folder. We're going to take a tour of the key elements in here.

layouts/default.twig

The first twig we'll look at is default.twig in the layouts directory. Open the file in your favourite text editor. If you have a text editor with syntax highlighting, switching it to HTML mode will make it a lot easier to inspect twigs.

default.twig is different from most twigs: the contents of this twig look almost like a normal web page with a head and a body. That's because default.twig is the basis for all the other twigs, like the trunk of a tree. No matter what page a user visits on your website, default.twig will be used as the basis to build it.

Amongst the <head> and <body> tags and the other things you'd expect to see in a HTML file, you'll see strange bits of syntax that aren't markup. These are the instructions – otherwise known as statements – that allow twigs to make choices and give orders. Anything you see between a {% %} is a statement for the template engine to follow.

Perhaps the most important statement is {% block content %} {% endblock %} which tells the template engine “whatever other twigs you add to this one, add them at this location in the page.” Depending on what page the user has asked to see, this content block might be filled with a bunch of blog posts, or just one, or a login form.

You'll also see conditional statements: {% if this %} {% else %} {% endif %}. If you've ever dealt with JavaScript , you'll understand what's going on here. The twig is telling the template engine to use different markup under different circumstances, for example the twig might choose to add some admin controls to the page if the visitor is logged in and has admin privileges.

The other oddity you'll see repeated in twigs is text inside a set of double curly brackets like this: {{ }}. These are variables that will result in the template engine replacing this bit of text with a corresponding value. For example, {{ theme.stylesheets }} will be replaced with a set of <link> tags to the theme's stylesheets, and {{ "Search" | translate }} will be replaced with the word “Search” possibly translated into another language if a translation file is available.

content/post.twig

You'll be seeing this twig a lot on your site, it's the twig that outputs the basic markup that surrounds all blog posts. The obvious choice with post.twig is to have it output an <article> element to contain each of your blog posts, so that you can easily arrange and style your posts using a stylesheet – and that's exactly what Blossom does.

A few lines into the twig, you'll see something familiar: {% block content %} {% endblock %}. What's going on here? Content blocks can be nested, so default.twig tells Chyrp where in the page to insert the next twig, and that next twig might be post.twig, in which case post.twig adds a few elements of its own to the page and also provides a place for the next twig to be inserted. That next twig will be different depending on the kind of post this is: text, photo, etc.

After the content block, this twig adds some metadata that are useful for any type of post: the date of the post, the tags, categories, and comments associated with it, and buttons to edit or delete the post. Remember what we learned about conditional statements when we looked at default.twig? Look closely at post.twig and you'll see a lot of conditional statements, displaying comments only if the commenting module of Chyrp is enabled, displaying the edit and delete buttons only if the user viewing the page has the rights to perform these actions, and so on.

If you're building a custom theme just for your own use and you don't want some of these features, don't include them – it's that simple. You can rearrange, add, remove, and customise the appearance of your blog as much as you like.

feathers/text.twig

Let's suppose a visitor to your site lands on the index page of your blog. The template engine will fill the page with several instances of post.twig (how many posts are displayed depends on your administration settings). We've seen that post.twig reserves a content block for the blog post but itself doesn't display blog posts, so what twig is responsible for that task? Well, if your post is plain text then text.twig is responsible.

text.twig is a special kind of twig that allows the template engine to render different types of content in your blog posts. Each twig in this folder corresponds to a Chyrp Lite Feather: an extension that enables Chyrp to support different content types in blog posts. Take a look in the feathers directory and you'll see the names of several other twigs dedicated to certain types of content – photos, quotes, links, and so on. The simplest kind of blog post is plain text, and that's what text.twig is here to display. First of all, this twig declares which twig it's here to extend: {% extends "content/post.twig" %}. Then we get down to business. Plain text is so simple that text.twig is very simple too. It adds a title element to the page if one exists, and it tells Chyrp where to insert the content of the post using the variable {{ post.body }}.

I'm not going to cover the other feathers. I think you'll be able to figure them out. Next up, we'll check out some forms.

forms/user/login.twig

This twig displays a form that allows visitors to your blog to log in, if they are registered users. Even if your blog doesn't allow users to register you'll still see this form every time you log in to write a post.

Firstly login.twig declares it's here to extend layouts/default.twig. This means any HTML this twig outputs will be inserted in default.twig's content block. The rest of the twig is a fairly straightforward HTML form with just a few variables such as {{ url('login') }} that will be replaced with the correct URL for logging in, and some translation filters such as {{ "Username" | translate }}. You will see translation filters quite often as we continue our tour of the Blossom theme.

forms/post/edit.twig

Wow, that's a big form. It's scary but we're going to look at it closely and once we're done everything will make sense. Firstly, what is this thing? Let's say you are a logged-in user and you are viewing a blog post that you have the right to edit or delete. You'll see some buttons (added by post.twig) that offer you those options. If you click on the edit button, the blog post will disappear, and in it's place will appear this form occupying exactly the same place on the page where the blog post had been.

The first new discovery here is a trigger. There's one near the top of this twig:

{{ trigger.call("before_ajax_edit_post_fields", feather) }}

The trigger will “fire” when the template engine processes this line in the twig. Think of a trigger like a JavaScript event. Triggers are used by Chyrp Lite and its extensions to do clever things in response to certain situations - and unless you know what you're doing it's probably best to reproduce each trigger in your custom theme exactly as you find it in the default theme.

After the trigger, there's a new type of Twig statement: a loop. The statement is {% for field in feather.fields %} and it works in a similar way to a JavaScript for or PHP foreach loop. For every field of data a feather has added to the post, the loop will inspect the field and add an appropriate form element to the page. For a plain text post, this will result in a <input> field for the post's title and a <textarea> containing the body of the post. There's a little conditional statement in this loop that adds the text “(optional)” to the label of form elements that are not required.

Next the twig adds a <select> element if the user has the right to set the status of a post. Then follow several form elements that will always be present no matter the type of post, such as pinned status, timestamp, and so on.

Next is another loop that creates elements for post options.

Finally, there's "Save" and "Cancel" buttons with translation filters, and some hidden <input> fields to hold other data needed by Chyrp Lite to process the form.

When the "Save" or "Cancel" buttons are clicked, this form and the entire contents of this twig will disappear and be replaced by the freshly updated blog post. It's important that the form element in this twig is the outermost element and has the attribute id="post_edit_form_{{ post.id }}" in order for inline editing to function correctly.

pages/view.twig

The pages directory contains many twigs of a type distinctly different from feathers and forms. All these twigs are templates that extend default.twig for different viewing contexts. What do I mean by a context? Well, let's say a visitor lands on the index page of your blog – that's one context. After the index page, perhaps the most commonly seen context is the "permalink" or view context; this is a page that displays just one particular blog post in isolation, and view.twig is responsible for that context.

Firstly view.twig announces that it's here to extend default.twig then it gets started adding content to the page. If the blog post being viewed has other posts before and after it chronologically, the twig adds links to them.

Next we have something new: {% include "feathers/" ~ post.feather ~ ".twig" %}. Actually, if you had been looking closely you would have seen something similar in default.twig. This is an include statement, and default.twig uses one to add a sidebar to the page. The include statement is similar to block but it's used in situations where the twig knows exactly what content it wants to add to the page, similar to a CSS @import rule that injects one stylesheet into another. A block says: “if you've got something to add, add it here.” An include says: “I want this specific thing injected into the page here.”

In this case the twig is telling Chyrp to inject the suitable twig for the Feather type of the blog post the visitor is viewing. For a text post that means text.twig, and because text.twig extends post.twig that means post.twig is coming along as well. What we'll end up with in the rendered page is a nest of twigs, each adding its own little bit of content to the page:

  • default.twig
    • view.twig
      • post.twig
        • text.twig

After the include statement, view.twig adds some content to the page only if Chyrp Lite's commenting module is enabled.

pages/index.twig

This is your blog's index, a very simple twig that fills the page with blog posts. The index is what visitors will see when they arrive at your blog. The number of posts on the page is controlled in the administration console, and if there is more than one page of posts, default.twig will add hyperlinks to the next and previous pages of the index (if applicable). If your blog is configured to display 4 blog posts per page, your page will be constructed as follows:

  • default.twig
    • index.twig
      • post.twig
        • text.twig
      • post.twig
        • text.twig
      • post.twig
        • text.twig
      • post.twig
        • text.twig

pages/search.twig

This is another simple twig that displays the results of a search. If your blog has a search bar, this is the page where visitors will come to view the search results. A for loop iterates through the search results and includes the appropriate twig for each of the blog posts to be displayed on the page.

javascripts/theme.js.php

The javascripts directory contains all the JavaScripts you want to make available to your theme's pages. Chyrp Lite will create a <link> to each stylesheet found in this directory and the template engine will add these to the head of default.twig.

You can add as many scripts as you like, and adding files with a “.js” file extension is ok. However the Blossom theme does something a little different. The one file in the Javascripts directory of this theme is theme.js.php. What's going on here? This is a normal JavaScript wrapped in PHP, which allows the file to be processed on the server before it is delivered to the visitor. Here's the structure:

<?php
    define('JAVASCRIPT', true);
    require_once dirname(dirname(dirname(dirname(__FILE__)))).DIRECTORY_SEPARATOR."includes".DIRECTORY_SEPARATOR."common.php";
?>    
// Insert your JavaScript below this line.

You don't need to wrap your scripts in PHP if you don't want to, but it can be useful if you are familiar with PHP and you want to add some server-side magic to your JavaScripts. Blossom uses PHP to translate the alt-text of the HTML elements it injects into the page.

While we're on the subject, it's also worth noting that Chyrp Lite uses the jQuery JavaScript library. jQuery comes bundled with Chyrp and is automatically available on every page of your blog, so you won't need to add it to the javascripts directory.

Further Reading

Here our tour ends. You haven't seen everything that's needed to build a Chyrp Lite theme, but you've seen enough to figure out the rest on your own. If you want more technical detail about themes, read the article Anatomy of a Theme and if you want to learn more about Twig read the Twig Reference.

Clone this wiki locally