Bolt CMS for designers
Tags: php, symfony, twig
Published: 2021-11-04 19:16:41

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.

**Homepage**  
*Upper*  
![A homescreen with a simple navigation bar and a prominent call to action.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ea40ruejgwh7e86l2c2.png)

*Lower*  
![A homescreen with a simple navigation bar and a prominent call to action.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqaraoxhij9ecryxn1va.png)

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.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p3okxrmkm8in22uytk8r.png)

## 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:

```twig
<!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:  
```yaml
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:  
```yaml
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:  
![Hello Stim World!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xyem3i8wow8vex4xkppo.png)

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:

```yaml
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. 
```twig
<!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:  
![The header with a simple navigation-bar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cuyrjfezzmbvhvyh1ktl.png)

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.

```yaml
# 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:
```twig
<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:  
![The featured component as a Blocks content type.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6q7xnnxeawmhjax0svox.png)

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:  
```twig	
{# 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 }}`.

![The featured component is now displayed on the homepage.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cpozjuzfy2k4cyx4nj1p.png)

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.

```yaml
# 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.

```twig	
<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`.

And here's the final result:  
![The homepage with the featured component and the footer.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/23qsgjj16xb8hmkxkd05.png)

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.