This is my second article on Bolt CMS. The previous dealt with content creation and editing, today we will look at how we implement a design for it. Let's do this!
Twig to the rescue
Bolt CMS is the Symfony-based CMS, bringing you an admin interface that is easy to use for content creators. In this article I will detail the designer-aspect of Bolt. Bolt uses the templating engine Twig, and it is this we will use to implement a design and build out an entire theme 🖼
A theme in Bolt is a set of Twig-files 🌿. Most often there is a Twig file for the homepage, one for a single entry of a Content Type, and one for a listings page of multiple entries. You can resolve to use just one Twig-file to serve as a generic catch-all. But today we are going to build 1 file:
- homepage.html.twig for our homepage
The design
The design we are going to use is one that I've done to be used in a Bolt CMS theme. If you want to use it and implement it anywhere else, go right ahead. I call this theme Stim. It's a simple and energetic design.
So here we have a mockup of our homepage. Moving on, we will look at how to implement the structure of this page. We will forgo the styling and just accept that this will be sorted out and instead focus on writing the Twig-files.
Theming structure
Bolt CMS sports a structure where you can have multiple themes in a project. So before we start writing the files, let's get a grasp of where to put them. In the root of your Bolt project there will be a public
folder. Inside of this there is a theme
folder inside of which we will create a new folder. We will call this folder Stim
:
Writing templates with Twig
We will start with writing homepage.html.twig. This is the homepage of the website. It will be the first page that is loaded when the user visits the website. It will have a simple navigation bar and a prominent call to action. But first we would like to see that it works:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=<device-width>, initial-scale=1.0">
<title>Stim Theme</title>
</head>
<body>
<h1>HELLO STIM WORLD!</h1>
</body>
</html>
And let's confgure Bolt and the theme to use this file:
Create ./public/theme/Stim/theme.yaml with the following content:
homepage_template: homepage.html.twig
Configure Bolt ./config/bolt/config.yaml to use this theme. By default it is set to theme-2021 or skeleton. Change to this:
theme: stim
You may need to run composer cache:clear
, so do that. If you haven't started a virtual server, go ahead and do that. I often use Symfony CLI and run my project with symfony serve -d
in the project root. Open the browser and you should see the homepage. If you get a timeout and you haven't run composer cache:clear
then do it.
In 127.0.0.1:8000 I get this result:
This is a sign that everything works. There's just one other thing we will configure before we get going with templating. For the sake of showing how a menu is genereated we will configure a menu for us in the ./config/bolt/menu.yaml
file:
main:
- label: Home
title: This is the <b>first<b> menu item.
link: homepage
class: homepage
- label: Pages
link: pages/
- label: Blog
link: blog/
- label: About Us
link: pages/about
homepage.html.twig
We will start by writing the navigation bar. It will contain a logo and a few links as they are specified in ./config/bolt/menu.yaml.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stim Theme</title>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,400;0,700;0,900;1,400;1,700&display=swap" rel="stylesheet">
<!-- The stylesheet for this design -->
<link rel="stylesheet" href="{{ asset('styles/styles.css') }}">
</head>
<body class="wrapper">
<header class="container">
<nav class="nav">
<div>
<a href="#" title="Go to Home">
<div class="unvisible-object">LOGO</div>
<img class="logo" src="{{ asset('assets/Logo.svg') }}">
</a>
</div>
<div class="navbar">
{{ menu() }}
</div>
</nav>
</header>
</body>
There are a few things to note here. First, we are using the asset()
function within double-brackets {{}}
. Double-brackets are a special syntax that allows us to use Twig variables and filters in our Twig-files. This is a very powerful feature of Twig. We can use Twig variables to pass data to our Twig-files. In this case we are passing the path of the logo and the stylesheet and have this asset-filter generate the correct URLs for us. Second, we are using another filter with menu()
to generate the menu. This is a Twig filter that is generated from the menu.yaml
file.
Filter is a type of function that outputs a value. It can be to generate URLs, generate random numbers, or generate HTML. It's essentially a function, but in Twig-files we are a bit special about it. 😉
So far we have the header done:
For the next section we will start to work with content provided from the theme-configuration and the database. So first we'll configure the theme for a call to action.
# Edit Call to Action
cta_heading: "Heading <br/> Call To Action"
cta_subheading: "CTA subheading with text to call for an action. This is a great CTA!"
cta_primary: "/about-us"
cta_primary_text: "Primary"
cta_secondary: "/"
cta_secondary_text: "Secondary"
# Template to use, as configured earlier:
homepage_template: homepage.html.twig
Next we can use these configurations in our template. We do this by using the double-bracket syntax and accessing them as theme.config_variable
. We will also use another Twig-syntax, the bracket-percent {% %}
structure, to check that the variable is set. This structure is used when we want to do stuff. So for our Call To Action we have this:
<main class="container">
<section aria-labelledby="cta-heading" class="cta self-center">
<img class="image-bg" src="{{ asset("assets/circle-s.png") }}" alt="">
<h1 id="cta-heading" class="cta-heading">{{ theme.cta_heading|raw }}</h1>
<p class="subheading">{{ theme.cta_subheading }}</p>
<div class="cta-actions">
{% if theme.cta_primary %}
<a href="{{ theme.cta_primary }}" class="button button-primary">{{ theme.cta_primary_text }}</a>
{% endif %}
{% if theme.cta_secondary %}
<a href="{{ theme.cta_secondary }}" class="button button-secondary">{{ theme.cta_secondary_text }}</a>
{% endif %}
</div>
</section>
</main>
With this awesome call to action we can now start to work with content. We will start by creating a new block
contentType in the admin interface. We will name it Featured Component and put in an image and content as this:
Next up, we can access this content in our template. We will again use the bracket-percent structure as we want to do something, such as get content and set it to a Twig variable. setcontent
is a Bolt-specific Twig-function that combines accessing content and setting a variable, so this works great for our use-case:
{# Continued belor the CTA section #}
<img class="image-bg image-bg--center-right" src="{{ asset("assets/circle.png") }}" alt="">
<img class="image-bg image-bg--center-low" src="{{ asset("assets/circle.png") }}" alt="">
{% setcontent block = ('blocks/featured-component') returnsingle %}
{% if block %}
<section aria-labelledby="featured-section" class="self-center pt-8 teaser">
<img class="teaser-image" src="{{ block|image }}" alt="">
<div class="teaser-card mt-3">
{{ block.content }}
</div>
</section>
{% endif %}
As you may have gleaned from the code passage above, setting the featured-component to the variable block makes it accessible in the template with the double-bracket syntax. We access the block image with a special Twig filter called image. The pipe provides the variable to the left of it to the function to the right (image). And the content of the block is accessed with right from the variable with {{ block.content }}
.
We've gotten pretty far to describe some of the basic Twig-functionalities. Let's add a footer where we will have some basic contact information, and let's just put that information in the theme.yaml file.
# Contact information
contact_email: contact@example.com
contact_phone: +1-555-555-5555
Then we can again access these configuration variables with theme.config_variable
in our template.
<footer class="footer">
<div class="footer-contents">
<a href="#" title="Go to Home"><div class="unvisible-object">LOGO</div><img class="footer-image" src="{{ asset("assets/Light Logo.svg") }}" alt=""></a>
<div class="footer-info">
{% if config.has('general/payoff') %}
<h4>{{ config.get('general/sitename') }}</h4>
{% endif %}
{% if theme.contact_email %}
<a href="mailto:{{ theme.contact_email }}">{{ theme.contact_email }}</a>
{% endif %}
{% if theme.contact_phone %}
<a href="tel:{{ theme.contact_phone }}">{{ theme.contact_phone }}</a>
{% endif %}
</div>
</div>
</footer>
There is finaly one new thing introduced here. It is how we can access the project wide configuration of a Bolt project. The file config\bolt\config.yaml
contains a variable for the project name that we can use. We get to it with the twig-variable config
. Calling has
on it we verify that is exists, and then we can access it with get
.
That's it for this article. Twig is a fine templating engine that I like to use. There is more to explore here, but we can save that for another day such as Twig-partials, a way to make reusable components. So have you tried out any templating engines, what did you like about it and what did you dislike? I'd love to hear from you.
Top comments (1)
There appears to be missing assets.