GRAV: a Flat File CMS

David G - DrupalSo a few months ago I purchased a Synology NAS device. Primarily for personal projects and as a means to store and be a medium to backup my development projects. Having a more robust local development environment at my house I wanted to setup a kind of intranet for my house. At my previous job they had a simple intranet page that listed current projects, a note taking application and tools to create or backup websites. This is similar to a Start Page in Google Chrome. In fact, many weeks ago I saw great examples of startpages on Reddit’s /r/startpages. So — all these factors actually made me want to create a simple 1 page website with links and embedded tools within it. After an initial simple attempt at this I decided to take a stab at developing this in GRAV — a flat-file CMS.

Initial Work

So after finding a startpage/intranet design that appealed to me I kinda yanked it’s markup and filled my own sites. I ended up with something that looks like this and additionally uses a bit of PHP.

Basic startpage using some design work that is not my own :).

Basic startpage using some design work that is not my own :).

On this startpage the whole content is literally 1 HTML page with some PHP enabled for processing 1 HTML form. I use the HTML form to perform a Wake On Lan request to my newly purchased NAS. For the purposes of this blog post I will not be going further into the PHP usage or FontAwesome usage I simply want to a show a simple 1 page PHP page I want to move to GRAV.

GRAV Basic Information

Grav has pretty GREAT documentation at All you need to understand the basic structure of a GRAV site can be found in the online docs.

If you want to install GRAV in the /grav directory of your Arvixe website simply follow these directions within the Hosting directions of the getgrav site.

I will add my own initial observations here (bear in mind I’ve only been playing with GRAV for about 24 hours, part of which I was asleep :D):

  • Upon install the Problems grav plugin said my caching directories could not be read. Assure your webserver can read/write the cache and subdirectories therein.
  • If you want to develop your own plugins for the GRAV CMS then understanding its Event Hooks system will be key in the GRAV Lifecyle for a page request.
  • Your content is placed page files as Markdown or other supported markup formats. Your content is wrapped by a GRAV Theme which can apply to your whole site or simply a page. Plugins can act upon and interact with the page building and viewing process.

A Custom GRAV Site

So on my local box I downloaded and installed GRAV from it’s Core zipfile to a custom domain called ak.ira in my Hosts file. I strive for “bright”, “intelligent” and “clear” code :D. Hence the site name.

I then created a custom GRAV theme for my website and altered my Users site.yaml file accordingly. Creating a theme is simply a matter of:

  1. including a folder with the proper name in the Themes folder, in this case akira.
  2. providing the requisite yaml files that describe your theme to GRAV
  3. choosing a toolchain or writing simple custom css files to either /css or /css-compiled directories. I installed SASS locally and use SCSS to generate compiled CSS to the css-compiled folder.
  4. providing the default template file for your site and any needed TWIG partial files.

Here is an example screenshot of my website folder structure (remember GRAV is a flat-file based CMS, there is no database!):

Grav core installation with a basic custom theme called Akira.

Grav core installation with a basic custom theme called Akira.

As you can see I have created the required folder structure to create my own site theme.

A few examples of my GRAV files are:

This file informs GRAV that my theme exists.

name: Akira
version: 1.0
author: David Gurba
description: This is a simple basic theme using SASS and Flex layout for a startpage.

This file tells GRAV that this directory structure is read-only (a security measure):

      type: ReadOnlyStream
        - user/themes/akira

Lets look at 3 files that are more interesting:


{% extends 'partials/base.html.twig' %}

{% block content %}
	{{ page.content }}
{% endblock %}


<!DOCTYPE html>
<html lang="en">
{% set theme_config = attribute(config.themes, config.system.pages.theme) %}
{% block head %}
    <meta charset="utf-8" />
    <title>{% if header.title %}{{ header.title|e('html') }} | {% endif %}{{ site.title|e('html') }}</title>
    {% include 'partials/metadata.html.twig' %}
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <link rel="icon" type="image/png" href="{{ url('theme://images/favicon.png') }}" />
    {% block stylesheets %}
        {% do assets.addCss(',300,700', 99) %}
        {% do assets.addCss(',500,700,400italic,500italic', 99) %}
        {% do assets.addCss(',500,700,400italic,500italic', 99) %}
        {% do assets.addCss('theme://css-compiled/akira.css', 103) %}
        {% do assets.addCss('theme://css/font-awesome.min.css',100) %}

        {% if browser.getBrowser == 'msie' and browser.getVersion >= 8 and browser.getVersion <= 9 %}
            {% do assets.addJs('theme://js/html5shiv-printshiv.min.js') %}
        {% endif %}
    {% endblock %}
    {{ assets.css() }}

    {% block javascripts %}
        {% do assets.addJs('jquery',101) %}
        {% do assets.addJs('',100) %}
        {# do assets.addJs('theme://js/akira.js') #}
    {% endblock %}
    {{ assets.js() }}

{% endblock head%}
<body id="top" class="{{ page.header.body_classes }}">
    <div id="sb-site">
        {% block header %}
        <header id="header">

                <div id="logo">
                    <h3><a href="{{ base_url == '' ? '/' : base_url }}">{{ }}</a></h3>
                <div id="navbar">
                    {% block header_extra %}{% endblock %}
                    {% block header_navigation %}
                    {# include 'partials/navigation.html.twig' #}
                    {% endblock %}
                    <span class="panel-activation sb-toggle-left navbar-left menu-btn fa fa-bars"></span>

        {% endblock %}

        {% block body %}
        <section id="body" class="{{ class }}">
            {% block content %}{% endblock %}
        {% endblock %}

        {% block footer %}
        <footer id="footer">
            <p><a href="">Grav</a> Akira Theme.</p>
        {% endblock %}
    {% block bottom %}
    $(function () {
        $(document).ready(function() {
            // No op.
    {% endblock %}

This file describes the major structure of any given HTML page and blocks within it which can be passed values if extended. By and large I copied this file initially from the default Antimatter theme provided with Grav.

  • Note in this file I can link Google Fonts css really easily. Or link directly to my compiled SASS generated css code as needed. This is enabled by the Assets plugin provided by GRAV.


$config: (
        colors: (
                plantation-white: #FFFFFE,
                far-horizon: #ECF1EF,
                ubuntu-red: #D95F69,
        font-family: 'oxygen',
        maxWidth: 800px,
        columns: 12,
        margin: 15px,
        breakpoints: (
                xs: "(max-width : 480px)",
                sm: "(max-width : 768px) and (min-width: 481px)",
                md: "(max-width : 1024px)  and (min-width: 769px)",
                lg: "(min-width : 1025px)"

@function map-deep-get($map, $keys...) {
  $value: $map;
  @each $key in $keys {
    $value: map-get($value, $key);
  @return $value;

body {
  font-family: map-deep-get($config, 'font-family');
  background-color: map-deep-get($config, 'colors', 'plantation-white');
hr {
  border-color: map-deep-get($config, 'colors', 'far-horizon');

h1 {
  color: map-deep-get($config, 'colors', 'ubuntu-red');

I provided my own SASS file to GRAV. My end goal is to write SASS to leverage the new CSS3 flexbox layout system to make a responsive intranet site. The GRAV documentation explains that how you design your site, what CSS tools you use to create your theme is up to you. Installing SASS is really easy as it’s a RubyGem.

  • I use PHPStorm as my IDE and with it I can create a File Watcher command to easily regenerate the compiled CSS whenever I make a change to my SASS code. I simply added a Watcher with the following Arguments:
PHPStorm SASS File Watcher configuration.

PHPStorm SASS File Watcher configuration.

If you dont want to read that image the important Argument values to scss is:

--no-cache --update $FileName$:$ProjectFileDir$/user/themes/akira/css-compiled/$FileNameWithoutExtension$.css

With all these pieces in place my functional GRAV site now is slightly less butt-ugly and appears as:

Final GRAV site with a page powered by the Akira in-progress theme.

Final GRAV site with a page powered by the Akira in-progress theme.

I clearly am not quite up to the single PHP page I had made before but I’ve laid the groundwork to finish building out my intranet/startpage using GRAV. My future goals are:

  • Re-implement the Wake On Lan feature of the PHP page.
  • Implement flexbox layout of related links.

Watch for future updates on GRAV as I work through these remaining goals (Let’s hope I get to them, this is simply a personal project).

Tags: , , , | Posted under Arvixe | RSS 2.0

Author Spotlight

David Gurba

I am a web programmer currently employed at UCSB. I have been developing web applications professionally for 8+ years now. For the last 5 years I’ve been actively developing websites primarily in PHP using Drupal. I have experience using LAMP and developing data driven websites for clients in aviation, higher education and e-commerce. If you’d like to contact me I can be reached at

Leave a Reply

Your email address will not be published. Required fields are marked *