Grav is an open source flat-file CMS platform, built by the RocketTheme Team. While there are plenty of great CMS platforms available, they are all mostly database-driven, which can be overkill for smaller websites. Instead of a database, Grav uses folders and a basic file structure, it is focused on speed, simplicity, and flexibility.
After reading all of the documentation and spending some time trying Grav out, I’m definitely sold and will be using the platform. I hope you will too.
WHAT WE’LL BE BUILDING
For the first part of this tutorial we’ll be building a one-page website, and in the second part, we’ll build a simple blog. I’ll assume you have a good understanding of HTML and CSS (or CSS preprocessors). We will not be getting into the styling of the themes but rather focus on Grav, and its functionality.
You can check out both of these themes on GitHub:
INSTALLING GRAV
Below are the very few requirements Grav needs in order for it to run:
- A web server (Apache, Nginx, LiteSpeed, Lightly, IIS, etc.)
- PHP 5.5.9 or higher
Download Grav Core with the Admin Panel Plugin and unzip the package in the webroot of your web server and you’re ready to roll.
PAGE TYPES
Grav comes with 3 types of pages out of the box:
STANDARD PAGE
These are typically single pages, such as blog posts, contact pages, error pages, etc. Grav assumes that any page is a standard page unless otherwise specified.
LISTING PAGE
These are basically a standard page that has a reference to a collection of pages, for an example, a blog listing page. Configuration settings for these pages include: order, number of items and whether or not pagination is enabled.
MODULAR PAGE
Modular pages build a single page from it’s child pages, allowing us to build one-page layouts from smaller modular pages.
FILE STRUCTURE
Generally speaking, the only folder you’ll use is the /user folder.
THE CONTENT
The /user/pages folder is where all of the content pages live. Each page is placed in its own folder, and folder names should reflect the page’s name, and also be a valid slug.
You can order pages by naming the page folders with a preceding number: 01.home, 02.blog. Page folders then contain a markdown file and media for the page. The name of the markdown file will reference the name of the theme’s template to be used to render the content, for example: home.md would look for a template named home.html.twig.
THE THEME
Themes can be found within the /user/themes folder. For a theme to function you’ll need:
- blueprints.yaml – a file which contains information about the theme.
- themename.php – a file which contains any logic your theme needs.
- themename.yaml – a configuration file used by the plugin to set options the theme might use.
- templates/ – a folder containing the Twig templates to render the pages.
You should also include and these are required if you plan to release a theme:
- CHANGELOG.md – a file that follows the Grav Changelog Format to show changes.
- LICENSE – a file containing the license to the theme.
- README.md – a file with documentation for the theme.
- screenshot.jpg – a 1009px x 1009px screenshot of the theme.
- thumbnail.jpg – a 300px x 300px screenshot of the theme.
This is also where the css, sass, fonts, images, and js folders for the theme reside.
THE TEMPLATES
Templates can be found in the /user/themes/themename/templates folder. These templates are Twig templates and will be used to render your pages.
THE BLUEPRINTS
Blueprints are located in the /user/themes/themename/blueprints folder. The files within this folder are YAML files used to extend and modify the admin plugin with custom forms to make updating the website simpler.
PART 1: ONE PAGER MODULAR THEME
Now that we have a basic understanding of how Grav works, let’s get started on our first Grav theme: a one page theme showcasing Grav’s awesomeness. Below is what our content and themes folder are going to look like:
CONTENT FILE STRUCTURE
├── 01.home
│ ├── _download
│ │ └── download.md
│ ├── _features
│ │ └── features.md
│ ├── _highlights
│ │ └── highlights.md
│ ├── _intro
│ │ └── intro.md
│ ├── _overview
│ │ ├── grav-logo.png
│ │ └── overview.md
│ └── home.md
THEME FILE STRUCTURE
├── blueprints
│ └── modular
│ ├── highlights.yaml
│ └── showcase.yaml
├── css
│ └── main.css
├── fonts
├── imgs
├── js
├── sass
├── templates
│ ├── home.html.twig
│ ├── modular
│ │ ├── download.html.twig
│ │ ├── features.html.twig
│ │ ├── highlights.html.twig
│ │ ├── intro.html.twig
│ │ └── overview.html.twig
│ └── partials
│ └── base.html.twig
├── blueprints.yaml
├── onepager.yaml
├── screenshot.jpg
└── thumbnail.jpg
CONFIG FILES
SITE.YAML
The site’s configuration file. We should never edit the default configuration files found in system/config. Instead, we overwrite the settings we’d like to change via creating our own configuration files within user/config. Below is a simple version of a site configuration file.
For more complex configurations, have a look at the documentation.
title: 'One Pager'
author:
name: 'Angie Vella'
email: 'email@email.com'
metadata:
generator: 'Grav'
description: 'Simple One Page Theme for Grav'
keywords: 'HTML, CSS, Grav, Theme, One Page'
author: 'Angie Vella'
robots: 'noindex, nofollow'
THEME FILES
As mentioned before, there are a couple of files Grav requires for a theme to function.
BLUEPRINTS.YAML
This file defines theme information and configuration options to be shown in the Admin panel. Blueprints are defined with YAML and below is our blueprints file for our One Pager Theme.
name: One Pager
version: 1.0.0
description: "A simple one page theme for Grav"
author:
name: Angie Vella
email: email@email.com
url: http://www.website.com
license: MIT
ONEPAGER.YAML
This file contains theme configuration, like the blueprints file this is also defined in YAML. For this example we’ll keep it super simple.
enabled: true
default_lang: en
THEME TEMPLATES
Now that we have the files the theme requires to function, it’s time to get started on the Twig templates. These templates will be used to render our content.
BASE.HTML.TWIG
The base template is just that: the template that will be the base of our theme. We’ll be extending this template within our other templates. Let’s break it down:
<html lang=”{{ theme_config.default_lang }}”> – Gets the configuration set in onepager.yaml.
{% block head %}{% endblock head %} – This defines an area in the base template, typically containing the stuff we put in the<head>element. Note that the head in {% endblock %} is not required, but can be used for readability.
<title>{{ site.title }}</title> – Pulls the configuration set in config/site.yaml.
<link rel=”canonical” href=”{{ page.url(true, true) }}”> – Sets a canonical URL for the page.
<link rel=”icon” type=”image/png” href=”{{ url(‘theme://img/favicon.png’) }}”> – Points to the site’s favicons, located in the theme/imgs directory.
{% block stylesheets %} – Within this block we register our stylesheets for the theme.
{{ assets.css() }} – This outputs the stylesheets we just registered.
{% block javascripts %} – Just like the stylesheets block this registers our JavaScript files for the theme. Note that the jQuery library comes bundled with Grav.
{{ assets.js() }} – Renders the scripts we just registered.
<a href=”{{ base_url}}”>OnePager</a> – Links to the home page.
{% for module in page.collection.modular() %}
<li><a href="#{{ module.header.anchor }}">{{ module.menu }}</a></li>
{% endfor %}
Runs over the collection defined withinhome.mdto create the navigation.
{% block content %}{% endblock %} – This is where content from other templates that extend this one will be.
{% block bottom %}{% endblock %} – We add our custom JavaScript initialization here.
And this is what the file looks like when it’s all put together:
<!DOCTYPE html>
<html lang="{{ theme_config.default_lang }}">
<head>
{% block head %}
<title>{{ site.title }}</title>
<link rel="canonical" href="{{ page.url(true, true) }}">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="{{ url('theme://img/favicon.png') }}">
{% block stylesheets %}
{% do assets.addCss('theme://css/main.css') %}
{% do assets.addCss('https://fonts.googleapis.com/css?family=Montserrat:400,700') %}
{% endblock %}
{{ assets.css() }}
{% block javascripts %}
{% do assets.addJs('jquery', '110') %}
{% do assets.addJs('theme://js/singlepagenav.min.js') %}
{% if browser.getBrowser == 'msie' and browser.getVersion >= 8 and browser.getVersion <= 9 %}
{% do assets.add('theme://js/html5shiv.min.js') %}
{% do assets.add('theme://js/respond.min.js') %}
{% endif %}
{% endblock %}
{{ assets.js() }}
{% endblock head %}
</head>
<body>
<header class="main-header">
<div class="wrapper">
<div class="logo">
<a href="{{ base_url}}">OnePager</a>
</div>
<nav class="main-nav">
<ul>
{% for module in page.collection.modular() %}
<li><a href="#{{ module.header.anchor }}">{{ module.menu }}</a></li>
{% endfor %}
</ul>
</nav>
</div>
</header>
<section class="content-wrapper">
{% block content %}{% endblock %}
</section>
<footer class="main-footer">
<p>©<script type="text/javascript">document.write(new Date().getFullYear());</script> OnePager Grav Theme.</p>
</footer>
{% block bottom %}
{{ assets.js('bottom') }}
<script>
$('.navigation-wrapper').singlePageNav({
offset: $('header.menu').outerHeight(),
updateHash: true,
currentClass: 'menu-active'
});
</script>
{% endblock %}
</body>
</html>
HOME.HTML.TWIG
The template used to render the home page (user/pages/01.home/home.md). It builds on top of the base template and adds anchors to the sections. These anchors will be defined within the modular markdown file’s Front Matter.
{% extends 'partials/base.html.twig' %}
{% block content %}
{% for module in page.collection() %}
<div id="{{ module.header.anchor }}"></div>
{{ module.content }}
{% endfor %}
{% endblock %}
INTRO.HTML.TWIG
The intro to our one page site. Simply pulls the content of the intro.md file using {{ content }}.
<div class="intro">
<div class="wrapper">
{{ content }}
</div>
</div>
HIGHLIGHTS.HTML.TWIG
The second section of our website. This pulls the content from the highlights.md file as well as the the custom header settings defined in the file’s YAML Front Matter (We’ll understand this better once we get into the content files).
<div class="highlights">
<div class="wrapper">
{{ content }}
{% for highlight in page.header.highlights %}
<div class="highlight">
<i class="fa fa-fw fa-{{ highlight.icon }}"></i>
<h4>{{ highlight.header }}</h4>
<p>{{ highlight.text }}</p>
</div>
{% endfor %}
</div>
</div>
OVERVIEW.HTML.TWIG
By now you should start seeing a theme (no pun intended). This template renders the third section of the website. It pulls the content from overview.md and also get the image from the pages folder (section folder since this is a modular theme).
<div class="overview">
<div class="wrapper">
<div class="text">
{{ content }}
</div>
<div class="image">
{% set image = page.media.images|first %}
{% if image %}
{{ image }}
{% endif %}
</div>
</div>
</div>
FEATURES.HTML.TWIG
Similar to the features template. This pulls the content defined within the features.md file and the file’s YAML Front Matter.
<div class="features">
<div class="wrapper">
{{ content }}
{% for feature in page.header.features %}
<div class="feature">
<i class="fa fa-{{ feature.icon }}"></i>
<h5>{{ feature.header }}</h5>
</div>
{% endfor %}
</div>
</div>
DOWNLOAD.HTML.TWIG
The last template of our theme, pulls the content from the download.md file and the button defined in the YAML Front Matter.
<div class="download">
<div class="wrapper">
{{ content }}
{% for button in page.header.buttons %}
<div class="button-wrapper">
<a class="button" href="{{ button.url }}">{{ button.text }}</a>
</div>
{% endfor %}
</div>
</div>
CONTENT FILES
Our theme is now complete. We can focus on our content.
HOME.MD
This file tells Grav which subpages to pull to assemble the modular page, and which order to display them in. The name of the file also tells Grav to use the home.html.twig template to render the page.
---
title: Home
content:
items: '@self.modular'
order:
by: default
dir: asc
custom:
- _intro
- _highlights
- _overview
- _features
- _download
---
INTRO.MD
Within the YAML Front Matter we define the title of the section and an anchor. The content is then defined using markdown.
---
title: Home
anchor: intro
---
# OnePager
## A Simple One Page Modular Theme for Grav
HIGHLIGHTS.MD
Another title and anchor are defined, then we also create a custom header option called highlights. Each highlight has a header, text, and an icon. These are then rendered using the assigned template (highlights.html.twig).
Looking at the template you can see how the for statement {% for highlight in page.header.highlights %} loops through the highlights page header and then pulls the header {{ highlight.header }}, text {{ highlight.text }}, and icon {{ highlight.icon }}. At the end of this tutorial we’ll create a blueprint so these values can be edited via a form within the Admin panel.
---
title: Highlights
anchor: highlights
highlights:
- header: 'Crazy Fast'
text: 'Performance is not just an after thought, we baked it in from the start!'
icon: fighter-jet
- header: 'Easy to build'
text: 'Simple text files means Grav is trivial to install, and easy to maintain.'
icon: database
- header: 'Awesome Technology'
text: 'Grav employees best-in-class technologies such as Twig, Markdown & Yaml'
icon: cubes
- header: 'Super Flexible'
text: 'From the ground up, with many plugin hooks, Grav is extremely extensible'
icon: puzzle-piece
---
### Built on top of Greatness
#### Four core tenants keep Grav focused
OVERVIEW.MD
Similar to the intro.md. Here we only define the title and anchor in the YAML Front Matter, together with the content in markdown syntax.
---
title: Overview
anchor: overview
---
### Fast, Extensible, Open Source!
#### Grav is a modern open source flat-file CMS
Grav is a modern open source flat-file CMS. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse suscipit ultrices ligula eget accumsan. Sed egestas augue a risus semper pretium non sit amet odio.
FEATURES.MD
Here we basically repeat what we done in the highlights.md.
---
title: Features
anchor: features
features:
- header: Markdown Syntax
icon: text-height
- header: Twig Templating
icon: code
- header: Smart Caching
icon: rocket
- header: Flexible Taxonomies
icon: tags
- header: Simple Install
icon: cloud-download
- header: Powerful Plugins
icon: cogs
- header: Intuitive UI
icon: dashboard
- header: File-Based
icon: file-text
- header: Documentation
icon: bookmark
- header: On Github
icon: github
- header: Responsive Design
icon: html5
- header: Awesomazing
icon: heart
---
### Stuffed full of Amazing Features
#### This is a non-inclusive smattering of them
DOWNLOAD.MD
By now you’ve gotten the hang of the the YAML front matter. These headers can be as simple or as complicated as you want or need them to be. In this file we’ve defined the title and anchor for this section, together with a custom header defining our download button.
---
title: Download
anchor: download
buttons:
- text: Download
url: https://getgrav.org/downloads
---
### Get Grav
BLUEPRINTS
At this point our Grav theme is fully functioning, and we’ve also added the content for our one page website. Blueprints allow us to extend and modify the Admin panel. They are YAML files, defining a custom form for the specified template which then saves the data from the content file’s front matter.
Going over the files below, you can see they are very readable thanks to YAML. They extend on top of the default.yaml blueprint provided by Grav, and create a new tab in the Admin panel for each of the pages (in this case the highlights page and the features page).
HIGHLIGHTS.YAML
title: Highlights
'@extends': default
form:
fields:
tabs:
fields:
advanced:
fields:
columns:
fields:
column1:
fields:
name:
default: modular/highlights
'@data-options': '\Grav\Common\Page\Pages::modularTypes'
overrides:
fields:
header.template:
default: modular/highlights
'@data-options': '\Grav\Common\Page\Pages::modularTypes'
highlights:
type: tab
title: Highlights
fields:
header.highlights:
name: highlights
type: list
label: Highlights
fields:
.icon:
type: text
label: Icon
.header:
type: text
label: Header
.text:
type: text
label: Text
FEATURES.YAML
title: Features
'@extends': default
form:
fields:
tabs:
fields:
advanced:
fields:
columns:
fields:
column1:
fields:
name:
default: modular/features
'@data-options': '\Grav\Common\Page\Pages::modularTypes'
overrides:
fields:
header.template:
default: modular/features
'@data-options': '\Grav\Common\Page\Pages::modularTypes'
features:
type: tab
title: Features
fields:
header.features:
name: features
type: list
label: Features
fields:
.icon:
type: text
label: Icon
.header:
type: text
label: Header
PART TWO – MNMM BLOG THEME
Since much of the code is repeated from the previous theme, we will only go over the new code.
CONTENT FILE STRUCTURE
├── 01.home
│ ├── _blog-post
│ │ ├── media.jpg
│ │ └── item.md
│ └── blog.md
THEME FILE STRUCTURE
├── css
│ └── main.css
├── fonts
├── imgs
├── js
├── sass
├── templates
│ ├── blog.html.twig
│ ├── error.html.twig
│ ├── item.html.twig
│ ├── modular
│ └── partials
│ ├── base.html.twig
│ └── blog_item.html.twig
├── blueprints.yaml
├── mnmm.php
├── mnmm.yaml
├── screenshot.jpg
└── thumbnail.jpg
PLUGINS
In the previous theme, we only used the plugins that come with the Grav Core + Admin Panel. For this theme we’ve added some more plugins;
- Feed – Lets you view a Grav Collection as RSS or Atom news feed.
- JS Comments – Enables you to add comments to your site with Discourse, Disqus, Facebook, Google+, IntenseDebate, and Muut comment systems.
- Pagination – Adds pagination to your site.
- Readingtime – Adds reading time to your pages.
- SimpleSearch – Enables you to search your site’s content.
CONFIG FILES
SITE.YAML
Our config files remain the same, the only thing that changed is the title in site.yaml.
THEME FILES
BLUEPRINTS.YAML
The Front Matter here was changed to reflect this theme.
MNMM.YAML
This file is completely unchanged.
THEME TEMPLATES
BASE.HTML.TWIG
The base template file is very similar to the previous theme. The only changes here are for content/aesthetic reasons. The Grav code remains the same and is explained it the previous section.
<!DOCTYPE html>
<html lang="{{ theme_config.default_lang }}">
<head>
{% block head %}
<title>{{ site.title }}</title>
<link rel="canonical" href="{{ page.url(true, true) }}">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="{{ url('theme://img/favicon.png') }}">
{% block stylesheets %}
{% do assets.addCss('theme://css/main.css') %}
{% do assets.addCss('https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700') %}
{% endblock %}
{{ assets.css() }}
{% block javascripts %}
{% do assets.addJs('jquery', '110') %}
{% do assets.addJs('theme://js/singlepagenav.min.js') %}
{% if browser.getBrowser == 'msie' and browser.getVersion >= 8 and browser.getVersion <= 9 %}
{% do assets.add('theme://js/html5shiv.min.js') %}
{% do assets.add('theme://js/respond.min.js') %}
{% endif %}
{% endblock %}
{{ assets.js() }}
{% endblock head %}
</head>
<body>
<header class="main-header">
<nav class="main-nav">
<div class="wrapper">
<div class="logo"><a href="{{ base_url}}">MNMM</a></div>
<div class="search">
{% if config.plugins.simplesearch.enabled %}
{% include 'partials/simplesearch_searchbox.html.twig' %}
{% endif %}
</div>
</div>
</nav>
<div class="site-title">
<div class="wrapper">
<h1>{{ site.title }}</h1>
</div>
</div>
</header>
<section class="main-wrapper">
{% block content %}{% endblock %}
</section>
{% block footer %}
<footer class="main-footer">
<div class="wrapper">
<span class="credits">©<script type="text/javascript">document.write(new Date().getFullYear());</script> MNMM Grav Theme.</span>
<span class="icons">
<a class="button" href=""><i class="fa fa-facebook-square"></i></a>
<a class="button" href=""><i class="fa fa-twitter-square"></i></a>
<a class="button" href=""><i class="fa fa-google-plus-square"></i></a>
<a class="button" href=""><i class="fa fa-pinterest-square"></i></a>
<a class="button" href=""><i class="fa fa-linkedin-square"></i></a>
<a class="button" href=""><i class="fa fa-instagram"></i></a>
<a class="button" href=""><i class="fa fa-github-square"></i></a>
{% if config.plugins.feed.enabled %}
<a class="button" href="{{ feed_url }}.atom"><i class="fa fa-rss-square"></i></a>
<!--<a class="button" href="{{ feed_url }}.rss"><i class="fa fa-rss-square"></i> RSS</a>-->
{% endif %}
</span>
</div>
</footer>
{% endblock %}
{% block bottom %}
{{ assets.js('bottom') }}
{% endblock %}
</body>
</html>
BLOG.HTML.TWIG
This template is use to render the blog listings page, which in this case is the home page. It loops through the child pages (blog items) and includes the blog_item.html.twig template as well as pagination.html.twig which adds the blog listings page navigation.
{% embed 'partials/base.html.twig' %}
{% set collection = page.collection() %}
{% block content %}
<div class="blog-list-wrapper">
<div class="listing">
{% for child in collection %}
{% include 'partials/blog_item.html.twig' with {'blog':page, 'page':child, 'truncate':true} %}
{% endfor %}
<div class="pagination-wrapper">
{% if config.plugins.pagination.enabled and collection.params.pagination %}
{% include 'partials/pagination.html.twig' with {'base_url':page.url, 'pagination':collection.params.pagination} %}
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% endembed %}
ERROR.HTML.TWIG
This template is rendered when a page is not found. The error plugin is used which is included with the Grav Core + Admin Panel package.
{% extends 'partials/base.html.twig' %}
{% block content %}
<div id="error">
<div>
<h2>{{ 'ERROR'|t }} {{ page.header.http_response_code }}</h2>
<p>
{{ page.content }}
</p>
</div>
</div>
{% endblock %}
ITEM.HTML.TWIG
The item template renders the blog item content as specified in the blog.md file.
{% embed 'partials/base.html.twig' %}
{% block content %}
<div class="blog-item-wrapper">
<div id="blog-item">
{% include 'partials/blog_item.html.twig' with {'blog':page.parent, 'truncate':false} %}
</div>
</div>
{% endblock %}
{% endembed %}
BLOG_ITEM.HTML.TWIG
The template that renders the blog item. In here we pull the date, post title, post content, reading time (using the Reading Time Plugin), post tags, social share buttons (using the Social Buttons Plugin), comments (using the JSComments Plugin), and post pagination.
<div class="blog-list-item">
<div class="blog-list-header">
<span class="blog-list-date">
<time class="date" datetime="{{ page.date|date("c") }}">
<span>{{ page.date|date("d") }}</span>
<em>{{ page.date|date("M") }}</em>
<em>{{ page.date|date("Y") }}</em>
</time>
</span>
{% if page.header.link %}
<h4 class="post-name">
{% if page.header.continue_link is not sameas(false) %}
<a href="{{ page.url }}"><i class="fa fa-angle-double-right u-url"></i></a>
{% endif %}
<a href="{{ page.header.link }}" class="u-url">{{ page.title }}</a>
</h4>
{% else %}
<h4 class="post-name"><a href="{{ page.url }}" class="u-url">{{ page.title }}</a></h4>
{% endif %}
</div>
<div class="list-blog-padding">
{% if page.header.continue_link is sameas(false) %}
<div class="post-content">
{{ page.content }}
</div>
{% if not truncate %}
{% set show_prev_next = true %}
{% endif %}
{% elseif truncate and page.summary != page.content %}
<div class="post-content">
{{ page.summary }}
</div>
<div class="meta">
{% if config.plugins.readingtime.enabled %}
<span class="reading">{{ page.content|readingtime }}</span>
{% endif %}
{% if page.taxonomy.tag %}
<span class="tags">
{% for tag in page.taxonomy.tag %}
<a href="{{ blog.url|rtrim('/') }}/tag{{ config.system.param_sep }}{{ tag }}">{{ tag }}</a>
{% endfor %}
</span>
{% endif %}
</div>
{% elseif truncate %}
<div class="post-content">
{% if page.summary != page.content %}
{{ page.content|truncate(550) }}
{% else %}
{{ page.content }}
{% endif %}
</div>
{% else %}
<div class="post-content">
{{ page.content }}
</div>
<div class="post-meta">
{% if page.taxonomy.tag %}
<span class="post-tags">
{% for tag in page.taxonomy.tag %}
<a href="{{ blog.url|rtrim('/') }}/tag{{ config.system.param_sep }}{{ tag }}">{{ tag }}</a>
{% endfor %}
</span>
{% endif %}
</div>
<div class="post-share">
{% if config.plugins.socialbuttons.enabled %}
{% include 'partials/socialbuttons.html.twig' with {'url':page.url} %}
{% endif %}
</div>
<div class="post-comments">
{% if config.plugins.jscomments.enabled %}
{{ jscomments() }}
{% endif %}
</div>
{% set show_prev_next = true %}
{% endif %}
</div>
</div>
{% if show_prev_next %}
<div class="post-pagination">
<div class="wrapper">
{% if not page.isFirst %}
<span class="lft"><a class="button" href="{{ page.nextSibling.url }}"><i class="fa fa-chevron-left"></i> Next</a></span>
{% endif %}
{% if not page.isLast %}
<span class="rgt"><a class="button" href="{{ page.prevSibling.url }}">Prev <i class="fa fa-chevron-right"></i></a></span>
{% endif %}
</div>
</div>
{% endif %}
CONTENT FILES
BLOG.MD
items: ‘@self.children’ – tells Grav to loop through the page’s subpages (the blog posts).
order: – Order configuration for the blog posts.
pagination – enables the pagination.
---
title: 'MNMM Grav Theme'
content:
items: '@self.children'
order:
by: date
dir: desc
limit: 5
pagination: true
feed:
description: 'Sample Blog Description'
limit: 10
pagination: true
---
# MNMM Grav Theme
ITEM.MD
This is what the blog post files look like. A title and date are specified, as well as taxonomy. In this case we add this post to the blog category and define its tags. After the YAML Front Matter, the blog post content is defined.
---
title: The Urban Jungle
date: 17:34 07/04/2014
taxonomy:
category: blog
tag: [travel, photography, city]
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ultricies tristique nulla et mattis. Phasellus id massa eget nisl congue blandit sit amet id ligula. Praesent et nulla eu augue tempus sagittis. Mauris faucibus nibh et nibh cursus in vestibulum sapien egestas. Curabitur ut lectus tortor. Sed ipsum eros, egestas ut eleifend non, elementum vitae eros. Mauris felis diam, pellentesque vel lacinia ac, dictum a nunc. Mauris mattis nunc sed mi sagittis et facilisis tortor volutpat. Etiam tincidunt urna mattis erat placerat placerat ac eu tellus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ultricies tristique nulla et mattis. Phasellus id massa eget nisl congue blandit sit amet id ligula. Praesent et nulla eu augue tempus sagittis. Mauris faucibus nibh et nibh cursus in vestibulum sapien egestas. Curabitur ut lectus tortor. Sed ipsum eros, egestas ut eleifend non, elementum vitae eros. Mauris felis diam, pellentesque vel lacinia ac, dictum a nunc. Mauris mattis nunc sed mi sagittis et facilisis tortor volutpat. Etiam tincidunt urna mattis erat placerat placerat ac eu tellus. Ut nec velit id nisl tincidunt vehicula id a metus. Pellentesque erat neque, faucibus id ultricies vel, mattis in ante. Donec lobortis, mauris id congue scelerisque, diam nisl accumsan orci, condimentum porta est magna vel arcu. Curabitur varius ante dui. Vivamus sit amet ante ac diam ullamcorper sodales sed a odio. Curabitur ut lectus tortor. Sed ipsum eros, egestas ut eleifend non, elementum vitae eros. Mauris felis diam, pellentesque vel lacinia ac, dictum a nunc.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ultricies tristique nulla et mattis. Phasellus id massa eget nisl congue blandit sit amet id ligula. Praesent et nulla eu augue tempus sagittis. Mauris faucibus nibh et nibh cursus in vestibulum sapien egestas. Curabitur ut lectus tortor. Sed ipsum eros, egestas ut eleifend non, elementum vitae eros. Mauris felis diam, pellentesque vel lacinia ac, dictum a nunc. Mauris mattis nunc sed mi sagittis et facilisis tortor volutpat. Etiam tincidunt urna mattis erat placerat placerat ac eu tellus. Ut nec velit id nisl tincidunt vehicula id a metus. Pellentesque erat neque, faucibus id ultricies vel, mattis in ante. Donec lobortis, mauris id congue scelerisque, diam nisl accumsan orci, condimentum porta est magna vel arcu. Curabitur varius ante dui. Vivamus sit amet ante ac diam ullamcorper sodales sed a odio.
CONCLUSION
After building these two themes, you should have a basic understanding of how Grav works. As I’ve already said, in my opinion Grav can be as simple or as complex as you want/need it to be. I look forward to seeing some awesome things being built with this is awesome platform!