Create a theme for a Grav site

I’ve redesigned the Grav Shopping Cart website. Hope you like it!

The old site had a nice theme for a landing page, but was not thought for a bigger site. I have now purchased a new HTML theme (I’m no designer!) which is pretty nice and has many designs for inner site sections, such a the blog and the help desk. Very clean, has my favourite colors and I’m happy about it.

I’d like to talk a bit how the process was.

Moving an existing HTML theme to Grav is a straightforward process for me, I’ve done it dozens of times now and I have a quite fixed flow.

I like to work at a file level instead of using the Admin, but the same concepts apply.

At first I just wanted to include the theme’s pages as they are. No custom content, just replicate the HTML theme, but within a Grav structure.

I picked the page I want to start with, the home page.

The home page

I created a templates/home.html.twig Twig template inside the theme’s templates folder. This represents a template specific for the home page. Usually the home is a pretty unique page on the site, so it probably deserves a dedicated Twig.

I copied the whole home page code there, from <html> to </html>.

I created a pages/01.home/ file, and in config/system.yaml I set home as home.alias. This tells Grav to load that page as the home.

Now loading the page in the browser showed up the content, but it was missing all CSS/JS and images. That’s because the theme had them hardcoded as /img/* or /css/* links.

Adding the correct assets links

In Grav the links are broken because they point to the home route, so instead of pointing to /user/themes/mytheme/img, they point to /img in the Grav root. I could have moved that folder to the Grav root, but it’s best to keep all theme related stuff under the theme.

So I searched within the page for assets and I changed the images references from img/logo.png to <img src="{{ url('theme://img/logo.png', true) }}" />.

Stylesheets require a bit more thought as there’s an asset pipeline we’ll want to enable at some point, so I’ll move them to a stylesheets block within the <head> tag.

    {% block stylesheets %}
        {% do assets.addCss('theme://css/styles.min.css', 100) %}
    {% endblock %}
    {{ assets.css() }}

Same applies to JavaScript files, with the additional requirement that some JS is loaded in the footer.

    {% block javascripts %}
        {% do assets.addJs('theme://js/vendor/modernizr.custom.js') %}
        {% do assets.addJs('jquery',101) %}

        {% do assets.addJs('theme://js/vendor/preloader.min.js', {group: 'bottom'}) %}
        {% do assets.addJs('theme://js/scripts.js', {group: 'bottom'}) %}
    {% endblock %}
    {{ assets.js() }}

And in the footer, just before the closing </body> tag:

    {{ assets.js('bottom') }}

Fine for assets!

The page show now show up perfectly fine.

Adding another page

Now I wanted to add another page, the Blog page. I repeated the process to add a templates/blog.html.twig file, pasted the HTML source, and created a pages/ page.

Now, while images links inside the pages still need to be migrated to Grav's assets syntax (or simply change the path) I didn't want to repeat the same work I did above for CSS and JS assets.

So I identified the common parts of the pages (header and footer), and moved them to the templates/partials/base.html.twig file.

Each page template then needs to extend partials/base.html.twig and just add their unique content.

Going on from here

Many more tasks await us now:

  • Tweaking long pages in modular pages
  • Avoid repeating blocks of code, using the page frontmatter to store values in arrays and loop over them in the Twig templates
  • Adding blueprints to the theme to edit those modular pages, if I later want to easily change them through the Admin panel
  • Organize images in a common /pages/images folder, for easier handling

and much more. Just started!