DEV Community

Anders Björkland
Anders Björkland

Posted on

Bolt CMS for designers

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.



A homescreen with a simple navigation bar and a prominent call to action.


A homescreen with a simple navigation bar and a prominent call to action.

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:

Folder structure of a Bolt project.

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">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=<device-width>, initial-scale=1.0">
    <title>Stim Theme</title>
    <h1>HELLO STIM WORLD!</h1>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 I get this result:

Hello Stim World!

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:

  - 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
Enter fullscreen mode Exit fullscreen mode


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">
    <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="">
    <link rel="preconnect" href="" crossorigin>
    <link href=",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') }}">
<body class="wrapper">
    <header class="container">
        <nav class="nav">
                <a href="#" title="Go to Home">
                    <div class="unvisible-object">LOGO</div>
                    <img class="logo" src="{{ asset('assets/Logo.svg') }}">
            <div class="navbar">
                {{ menu() }}
Enter fullscreen mode Exit fullscreen mode

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:

The header with a simple navigation-bar

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
Enter fullscreen mode Exit fullscreen mode

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 %}
Enter fullscreen mode Exit fullscreen mode

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:

The featured component as a Blocks content type.

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 }}
{% endif %}
Enter fullscreen mode Exit fullscreen mode

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 }}.

The featured component is now displayed on the homepage.

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_phone: +1-555-555-5555
Enter fullscreen mode Exit fullscreen mode

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 %}
Enter fullscreen mode Exit fullscreen mode

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.

And here's the final result:

The homepage with the featured component and the footer.

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.

Discussion (0)