Grain theme is a project that provides a layout-based skeleton for creating static websites by only adjusting the configuration and adding a content. This post shows how to use the theme template in order to create a custom Grain theme — starting from defining content files and page layouts, and up to building user-friendly, page-formatting tools.
You should consider that to build custom website without using a ready-made theme, it is required to know Groovy or at least Java. Do not hesitate to give it a try if you are not familiar with Groovy yet.
Core Grain concepts
Before we proceed further, there are a few core concepts worth discussing to make it easier to understand how Grain works: resources, components and configuration.
Resources. Resource is a single website page, javascript file, stylesheet, image or any other raw source meant to be processed by Grain and used to generate website pages. Resources can be a part of a theme or site content. Generally, the rule is that resources shipped with a theme should not contain site-specific content and styles. The structure of resources by default corresponds to the website pages hierarchy.
Components. Grain ships with a variety of components that provide different resource processing methods. For instance, thanks to RST processor, it is possible to generate HTML content from .rst files, Pygments highlighter allows to apply syntax highlighting to website pages, and so on. Some of the components make it possible to heavily customize pages from a theme. Оne of them is the resource mapper which aims to handle resources in order to provide various pseudo-dynamic features, like pagination.
Configuration. Configuration defines which components process which resources, thus binding them in a certain way to provide a specific theme implementation. Grain comes with a default configuration, but it is also possible to change the settings on a per-project or system-wide basis.
Website theme template
Grain provides a handy template that can be used to start building a custom website or site theme from scratch. The template ships with all the necessary features and makes it a lot easier to utilize Grain components.
The theme template comes with:
- default configuration that is applicable for building websites
- snippets that show how to use theme resources (stylesheets, images and javascripts)
- raw content files that show common use cases for handling the site content
You can download the Grain Theme Template v0.4.0 from the Grain Themes page. It is not required to have Grain installed to start working with the template since the framework will be downloaded automatically as a dependency.
Grain framework is equally well applicable for implementing a static website and generating a project documentation. Although we will discuss only the basics of creating custom static sites, the use cases listed below can be effectively applied to any other Grain project, for instance to build a custom documentation generator.
Creating a custom page
Grain allows to specify custom commands through command-line extension component. The theme template utilizes this mechanism to provide a useful command for creating new pages:
./grainw create-page 'page' 'page title'
Code language: JavaScript (javascript)
- page – relative path to the new page, must start with ‘/’
- page title – the page title
To create the new Contacts page, navigate to the template directory and execute create-page
as follows:
# This will add the 'index.html' file to the '/content/contacts' folder.
./grainw create-page /contacts/index.html Contacts
Code language: PHP (php)
Then, run preview mode ./grainw preview
and open newly created http://localhost:4000/contacts/ page.
Grain determines the content markup type based on a file extension, besides HTML and XML, it is also possible to use Markdown, RST or any other markup currently supported by Grain components.
Page as a Grain resource
It is time to dive deeper into Grain and find out what features are available for page or resource processing and what actually happens when you run create-page
command.
Navigate to the /content/contacts directory of your project and open the index.html file. As you can see, this file does not contain any HTML markup, instead it has YAML content separated by ---
. This content is called a resource header. Resource header provides the static page configuration or the model. This configuration can be accessed within the page content through the page
variable in embedded Groovy code.
As an example, let’s add some contact information to the contacts page. At first, specify a map of contacts in YAML header:
/content/contacts/index.html
---
# ...
contacts:
Email: 'contact@mail.com'
Skype: 'skype.contact'
Phone: '(999) 999-999-99'
---
Code language: PHP (php)
Then, put the following code and markup right below the header:
/content/contacts/index.html
<!-- Gets the value of the 'title' property from the 'page' map. -->
<h1>${page.title}</h1>
<ul>
<!-- Iterates the contacts specified in the header and creates HTML markup. -->
<% page.contacts.each { contact, value -> %>
<li><em>${contact}</em>: ${value}</li>
<% } %>
</ul>
Code language: HTML, XML (xml)
Refresh the http://localhost:4000/contacts/ page to see the result.
There are a few more properties added by the create-page command to the page header: layout and published. To understand these variables, you should have a look at the layout and resource mapper components that are briefly described in the next sections.
Creating a custom page template
Page template is called a layout in Grain. Layouts provide generic page markup and are used to create new custom pages. By default, page layouts come as a part of a theme and are located in the /theme/layouts folder.
Layout can be applied to a page by specifying layout
property in the page header:
---
layout: default
# ...
---
Code language: PHP (php)
When a page applies a layout:
- the layout is rendered instead of the page
- page content is passed to the layout’s
content
variable - page header is merged with the layout header
Let’s create the product template that will define markup for product marketing pages. Open the /theme/layouts directory and create the product.html file:/theme/layouts/product.html
/theme/layouts/product.html
---
layout: default # applies the default layout
title: unknown # will be overridden by the 'title' page property
---
<!-- Gets properties from a page header. -->
<h1>${page.title}</h1>
<h3>${page.subtitle}</h3>
<ul>
<!-- Iterates the features specified in the page header. -->
<% page.features.each { feature, description -> %>
<li><strong>${feature}</strong><br />${description}</li>
<% } %>
</ul>
<!-- Inserts the page content. -->
${content}
Code language: HTML, XML (xml)
Note that it is not required to copy the content provided by the default template, you can just apply it by specifying the layout: default
property in the product layout header. Also, it is worth mentioning that the page model is available through the layout’s page
map, since the page header is merged with the layout header.
Put the newly created template in use, navigate to the /content/product
folder and create the following index.html file which provides the appropriate model and the content for the layout:
/content/product/index.html
---
layout: product
title: Product sample
subtitle: Brief product description
features:
Feature 1: |
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor.
Feature 2: |
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat.
published: true
---
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation. </p>
Code language: JavaScript (javascript)
Finally, open the http://localhost:4000/product/ page to see the result.
Creating a custom website configuration
Grain project configuration can be defined in the SiteConfig.groovy
class which you can find in the root directory of the theme template. The SiteConfig.groovy
is a ConfigSlurper file where settings can be specified using a dot notation and scoped with a help of closures:
SiteConfig.groovy
// Grain project directories.
cache_dir = "${base_dir}/.cache"
content_dir = "${base_dir}/content"
theme_dir = "${base_dir}/theme"
// Environment dependent configuration.
environments {
// Settings for 'preview' mode.
dev {
log.info "Development environment is used"
}
// Settings for 'generate' mode.
prod {
log.info "Production environment is used"
}
}
Code language: JavaScript (javascript)
The website configuration is available in any project resource through the site
map. For instance, you can declare the site_author
property in the SiteConfig.groovy:
SiteConfig.groovy
site_author = 'John Doe'
Code language: JavaScript (javascript)
And then access the value of the property in the default layout to display a copyright notice:
/theme/layouts/default.html
<!-- Gets a value of the 'site_author' property using the 'site' map -->
Copyright © ${new Date().format('yyyy')} ${site.site_author}
Code language: HTML, XML (xml)
It is also possible to manage the configuration of all the Grain components though the ‘SiteConfig.groovy’ when required. For more details, please, refer to the documentation.
Basics of resource processing in Grain
Grain provides a variety of resource customization components that allow to implement highly demanding features in a quick and timely manner. The most essential of them are the tag library and the resource mapper.
Tag library component allows to inject methods (also called as tags) into website resources. Tags are very helpful when it comes to content processing that can be done only within the website pages.
Any Grain tag is just a closure declared in a tag library class. Let’s navigate to the /theme/sources/ directory and open the ThemeTagLib.groovy
file which is the default tag library of the theme template. Then, add the capitalize
tag by specifying the following closure:
ThemeTagLib.groovy
/**
* Capitalizes all the words in the given text.
*
* @attr text the text to capitalize
*/
def capitalize = { Map attrs ->
if (!attrs.text) throw new
IllegalArgumentException('Tag [capitalize] is missing required attribute [text]')
WordUtils.capitalizeFully(attrs.text)
}
Code language: JavaScript (javascript)
The newly created capitalize
tag will be available from within all the site resources. For example, you can call it from the product layout to convert the page header:
/theme/layouts/product.html
<h1>${capitalize text: page.title}</h1>
Code language: HTML, XML (xml)
Resource mapper component is the last thing you should learn before start building websites with Grain. Resource mapper allows to customize page models, dynamically create new resources and change page URLs and markup types. You can find detailed description of the resource mapper component in the post Creating paginated archives in Grain.
Conclusion
Now, when you are familiar with the most vital Grain concepts and components, it is a time to start developing your own site and exploring advanced Grain features. Please, follow us on twitter to keep up on the latest news and updates and don’t forget to leave a feedback!