Often, the best way to learn a new thing is to use an example, and then try to build your own creation from it. We are going to use this same methodology for creating a new Grav theme.
Quark
Grav comes with a clean and modern theme called Quark which uses the Spectre.css framework.
Spectre.css is a lightweight, responsive and modern CSS framework for faster and extensible development.
Spectre provides basic styles for typography and elements, flexbox based responsive layout system, pure CSS components and utilities with best practice coding and consistent design language.
However, it's often better to start from something even simpler.
Pure.css
For the sake of this tutorial, we will create a theme that utilizes the popular Pure.css framework developed by Yahoo!
Pure is a small, fast, and responsive CSS framework that contains the basics to get you developing your site without the overhead of larger frameworks such as Bootstrap or Foundation. It contains several modules that can be used independently, but all together the resulting package is only 4.0KB minified and gzipped!
You can read up on all the features Pure brings to the table on the Pure.css project site.
Also, you should read the Important Theme Updates blog article that outlines some key changes in Grav themes to provide the best plugin support going forward.
Step 1 - Install DevTools Plugin
Previous versions of this tutorial required creating a base theme by default. This whole process can be skipped thanks to our new DevTools Plugin
The first step in creating a new theme is to install the DevTools Plugin. This can be done in two ways.
Install via CLI GPM
Navigate in the command line to the root of your Grav installation.
After logging in, simply navigate to the Plugins section from the sidebar.
Click the Add button in the top right.
Find DevTools in the list and click the Install button.
Step 2 - Create Base Theme
For this next step you really do need to be in the command line as the DevTools provide a couple of CLI commands to make the process of creating a new theme much easier!
From the root of your Grav installation enter the following command:
Enter Theme Name: MyTheme
Enter Theme Description: My New Theme
Enter Developer Name: Acme Corp
Enter Developer Email: contact@acme.co
Please choose a template type
[pure-blank ] Basic Theme using Pure.css
[inheritance] Inherit from another theme
[copy ] Copy another theme
pure-blank
SUCCESS theme mytheme -> Created Successfully
Path: /www/user/themes/my-theme
[/prism]
The DevTools command tells you where this new template was created. This created template is fully functional but also very simple. You will want to modify this to suit your needs.
In order to see your new theme in action, you will need to change the default theme from quark to my-theme, so edit your user/config/system.yaml and change it:
Reload your site in your browser and you should see the theme has now changed.
Step 3 - Theme Basics
Now we've created a new basic theme that can be modified and developed, let's break it down and have a look at what makes up a theme. If you look in the user/themes/my-theme folder you will see:
This is a sample structure but some things are required:
Required Items to Function
These items are critical and your theme will not function reliably unless you include these in your theme.
blueprints.yaml - The configuration file used by Grav to get information on your theme. It can also define a form that the admin can display when viewing the theme details. This form will let you save settings for the theme. This file is documented in the Forms chapter.
my-theme.php - This file will be named according to your theme, but can be used to house any logic your theme needs. You can use any plugin event hook except onPluginsInitialized(), however there is a theme specific onThemeInitialized() hook specific for themes that you can use instead.
my-theme.yaml - This is the configuration used by the plugin to set options the theme might use.
templates/ - This is a folder that contains the Twig templates to render your pages.
Required Items for Release
These items are required if you wish to release your theme via GPM.
CHANGELOG.md - A file that follows the Grav Changelog Format to show changes in releases.
LICENSE - a license file, should probably be MIT unless you have a specific need for something else.
README.md - A 'Readme' that should contain any documentation for the theme. How to install it, configure it, and use it.
screenshot.jpg - 1009px x 1009px screenshot of the theme.
thumbnail.jpg - 300px x 300px screenshot of the theme.
Step 4 - Base Template
As you know from the previous chapter, each item of content in Grav has a particular filename, e.g. default.md, which instructs Grav to look for a rendering Twig template called default.html.twig. It is possible to put everything you need to display a page in this one file, and it would work fine. However, there is a better solution.
Utilizing the Twig Extends tag you can define a base layout with blocks that you define. This enables any twig template to extend the base template, and provides definitions for any block defined in the base. So look at the templates/default.html.twig file and examine its content:
First, the template extends a template located in partials/base.html.twig.
You don't need to include templates/ within Twig templates as Twig is already looking in templates/ as the root level for any template.
Second, the content block is overridden from the base template, and the page's content is output in its place.
For consistency, it's a good idea to use the templates/partials folder to contain Twig templates that represent either little chunks of HTML, or are shared. We also use templates/modular for modular templates, and templates/forms for any forms. You can create any sub-folders you like if you prefer to organize your templates differently.
If you look at the templates/partials/base.html.twig you will see the meat of the HTML layout:
[prism classes="language-twig line-numbers"]
{% set theme_config = attribute(config.themes, config.system.pages.theme) %}
<!DOCTYPE html>
{% block head %}
{% if header.title %}{{ header.title|e }} | {% endif %}{{ site.title|e }}
{% include 'partials/metadata.html.twig' %}
{% endblock head %}
{% block stylesheets %}
{% do assets.addCss('http://yui.yahooapis.com/pure/0.6.0/pure-min.css', 100) %}
{% do assets.addCss('https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css', 99) %}
{% do assets.addCss('theme://css/custom.css', 98) %}
{% endblock %}
{% block javascripts %}
{% do assets.addJs('jquery', 100) %}
{% endblock %}
{% block assets deferred %}
{{ assets.css()|raw }}
{{ assets.js()|raw }}
{% endblock %}
{% block header %}
{% endblock %}
{% block footer %}
{% endblock %}
{% block bottom %}
{{ assets.js('bottom')|raw }}
{% endblock %}
[/prism]
! **TIP:** If a variable is safe to render and contains HTML, always use the `|raw` filter to make the template work with `autoescape` turned on.
!! It is very important to either turn on the `autoescape` setting in [System Configuration](/basics/grav-configuration#twig) or to remember to escape every single variable in template files to make your site safe against [XSS attacks](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting).
## Step 5 - Breaking it Down
Please read over the code in the `base.html.twig` file in order to better understand what is happening. There are several key things to note:
1. A `theme_config` variable is set with the theme configuration. Because Twig doesn't work well with dashes, to retrieve variables with dashes (e.g. `config.themes.my-theme`), we use the `attribute()` Twig function to dynamically retrieve the `my-theme` data from `config.themes`.
1. The `` tag.
1. The `` tag is dynamically set based on the page's `title` variable as set in the page header. The `header.title` is a shortcut method but is equivalent to `page.header.title`.
1. After a couple of standard meta tags are set, there is a reference to include `partials/metadata.html.twig`. This file is located in the `systems/templates/partials` folder and contains a loop that loops over the page's metadata. This is actually a merge of metadata from `site.yaml` and any page-specific overrides.
1. The `` tag has a class attribute that will output anything you set in the `body_classes` variable of the page's frontmatter.
1. The `header` block has a few things that output the HTML header of the page. One important thing to note is the logo is hyperlinked to the `base_url` with the logic: `{{ base_url == '' ? '/' : base_url }}`. This is to ensure that if there is no subdirectory, the link is just `/`.
1. The title of the site is output as the logo in this example theme with `{{ config.site.title }}` but you could just replace this with a `` tag to a logo if you wanted.
1. The `