A port of the Drupal zen base theme with css-grid, libsass and gulp support.
Branch: master
Clone or download
Type Name Latest commit message Commit time
Failed to load latest commit information.
archetypes Updated default archetype. Jul 16, 2017
exampleSite Added an exampleSite. Mar 23, 2018
layouts Some small dir rtl improvments. Feb 14, 2019
php Move contact.php.example file out of static dir. Mar 9, 2018
sass Some small dir rtl improvments. Feb 14, 2019
static Added on touchmove to contact.js. Jun 14, 2018
.eslintrc First commit. Mar 9, 2017
.gitignore First commit. Mar 9, 2017
.sass-lint.yml Updated the normalise css. Jan 30, 2018
LICENSE.txt First commit. Mar 9, 2017
create_sub_theme.sh Adding script and instructions for creating zen sub themes. (Supporte… Jul 8, 2018
gulpfile.js Add command to touch the generated css files to set changed date to now. Oct 15, 2018
package.json Updated npm packages. Nov 30, 2018


The Hugo Zen theme

Zen theme strives to be as clean and standard compliant as possible with some neat features. A solid base for your custom Hugo theme.

It uses HTML5 with a modern CSS grid and flex layout. Recent versions of all the mayor browsers support it, see Can I use css grid.

This is a port of the Zen theme by JohnAlbin, a very popular base theme for Drupal.

The old branch has the original Zen grid system that uses floats and clearfixes like they did in the stone age.


  • A mobile menu
  • Analytics with Matamo (Piwik)
  • Automatic linting of css and js
  • Contact form (PHP)
  • CSS grid and flex for layout
  • Gulp.js
  • HTML5
  • jQuery 3
  • libSass
  • Micro.blog
  • Minify css
  • Meta tags and JSON-LD
  • Multilingual (i18n)
  • Normalize CSS
  • Responsive design
  • RSS and JSON feeds with full content
  • Search with DuckDuckGo
  • Sub theme support (Theme Components)

Sites using the Hugo Zen theme

On the large screen


On the small screen


The mobile menu



$ cd themes
$ git clone https://github.com/frjo/hugo-theme-zen.git zen

Hugo - Installing Hugo

Create a zen subtheme

Since Hugo 0.42 there is sub them support via Theme Components. This makes it easy to update the base theme without overwriting customisation.

Navigate to the theme folder where you have placed the zen base theme. Run the included "create_sub_theme.sh" script like this.

$ ./zen/create_sub_theme.sh

Enter a name for your sub theme when asked for. In the site config file add your sub theme name first and "zen" after it. Hugo will now first look in the sub theme for files and if they are not there look in the zen base theme.

Edit all css/sass only in the sub theme and add any custom layouts etc. If there is a need to edit e.g. layouts from the zen base theme make sure to copy them over to the sub theme and edit them there.

This way all customisation are in the sub theme, making it easy to update the base theme.

config.yaml example

All the "params" are optional.

baseurl: "https://example.org/"
title: "SiteTitle"

  cacheBustCSS: true        # Add a cache busting hash on styles.css
  cacheBustJS: true         # Add a cache busting hash on script.js
  contact: "info@example.org"
  copyright: "This site is licensed under a 
              [Creative Commons Attribution-ShareAlike 4.0 International License]
  dateformat: ""            # Set the date format, default to "2 January, 2006"
  description: ""           # Set site description, used in meta tags and JSON-LD
  favicon: ""               # Relative path to favicon in json feed, no leading slash.
  feedlinks: true           # Show feed links in the footer.
  footer: "A [example.org](https://example.org/) production."
  icon: ""                  # Relative path to icon in json feed, no leading slash.
  image: ""                 # Relative path to site image in JSON-LD, no leading slash.
  imageMaxWidth: ""         # Max width for images added via figure shortcode.
  jquery: true              # Add jQuery
  languageDir: ""           # Set ltr or rtl, defaults to ltr.
  logo: false               # Turn off the logo.
  microUsername: ""         # Your micro.blog username.
  mobileMenu: true          # Turn on a mobile menu on small screens.
  piwikSiteId:              # Matamo site id
  piwikTrackerUrl: ""       # Matamo url, schemaless and no slash on end (example.com/matamo).
  poweredby: true           # Show powered by hugo in footer
  search: true              # Site search with DuckDuckGo.
  searchSize: 20            # Search field size, default 20.
  sidebar: true             # Show a sidebar to the right
  submitted: true           # Show author and date information for a post.


English and Swedish translations are included and you can easily add more to the i18n site directory.

A language selector will be included on sites with more than one language. Add a LanguageName parameter to your language configuration, this is what will be displayed in the selector.

The language selector will link to a translation of the current page if it exist and to the front page if it does not.

    weight: 1
      LanguageName: "Svenska"
    weight: 2
      LanguageName: "English"

Contact form

If your server support php with the mail() command (very common) you can use the included contact form feature to get a contact form for your site.

  1. Copy the file themes/zen/php/contact.php.example to static/php/contact.php.
  2. Edit the contact.php file so it has your own e-mail address. You may also change the mail subject prefix.
  3. Add the shortcode {{< contact >}} to a page. Also set contactform: true in the front matter for that page so the contact.js file gets loaded.

If you have a SPF record for your domain, make sure the web server is listed or other mail server may mark the mail as spam.

Two types of spam protection is implemented. The form can only be posted after the user have moved the mouse or pressed the tab or enter key. The form have a "honypot" field that is invisible to humans but not to most spam boots. If that field is filled in the mail will not be sent.

Form validation is handeld by HTML5 and there is some CSS to make it look nice.

Javascript is used for spam protection and to display error/success messages.


If "lastmod" is set in the frontmatter on a post that value will be used in the "submitted" section. If not "date" is used.

With "lastmod" set a date section will also appear at the bottom of post telling the reader the created and modification dates.



Insert a html5 contact form, see more above.

{{< contact >}}


Zen comes with a improved version of the built in "figure" shortcut.

  • You can set a max width for images with parameter "imageMaxWidth".
  • If width and height is not set the real dimensions of the image will be used.
  • If only width or height is set the other value will be proportionally calculated.


Break float.

{{< figure src="/images/image.jpg" class="right" >}}

blablabla # Displayed left of the image.

{{< clear >}}

blablabla # Displayed below of the image.


A simple, but useful, shortcode to wrap content in a div with a class.

{{% wrapper class-name-you-want %}}
The **content** that should be wrapped.
{{% /wrapper %}}

This will produce:

<div class="class-name-you-want">
The <strong>content</strong> that should be wrapped.

Use Gulp to generate css from sass

Your new Zen theme uses Gulp.js as a task runner, so that it can do many different tasks automatically:

  • Build your CSS from your Sass using libSass and node-sass.
  • Add vendor prefixes for the browsers you want to support using Autoprefixer.
  • Build a style guide of your components based on the KSS comments in your Sass source files.
  • Lint your Sass using sass-lint.
  • Lint your JavaScript using eslint.
  • Minify the css.
  • Watch all of your files as you develop and re-build everything on the fly.

Set up your front-end development build tools:

  1. Install Node.js and npm, the Node.js package manager.

    Detailed instructions are available on the "npm quick start guide": https://github.com/kss-node/kss-node/wiki/npm-quick-start-guide

  2. The package.json file in your new sub-theme contains the versions of all the Node.js software you need. To install them run:

     npm install
  3. Install the gulp-cli tool globally. Normally, installing a Node.js globally is not recommended, which is why both Gulp and Grunt have created wrapper commands that will allow you to run "gulp" or "grunt" from anywhere, while using the local version of gulp or grunt that is installed in your project. To install gulp's global wrapper, run:

     npm install -g gulp-cli
  4. The default gulp task will build your production minified CSS, build your style guide, and lint your Sass and JavaScript. To run the default gulp task, type:


    To watch all your files as you develop, type:

     gulp watch

    or just type:

     gulp watch:css

    to only watch the css files.