EJS

CMintS is using EJS as a templating engine for creating layouts, EJS can also be used for the page creation. EJS is a simple templating language that lets you generate HTML markup while writing plain JavaScript. Detailed EJS syntax documentation can be found here, also there is an online playground, to try out the syntax.

Layout

As was mentioned in the themes overview in order to decide which layout to use for the page, a Front Matter "layout" property needs to be used, which falls back to the default layout.

Considering snippet below being theme/layouts/default.ejs:

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" type="text/css" href="/css/main.css">
</head>
<body>
<main>
  <%- page.body %>
</main>
</body>
</html>

And snippet below being pages/about.md:

# about
This is the about page

The request to the /about page will generate HTML below:

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" type="text/css" href="/css/main.css">
</head>
<body>
<main>
  <h1>about</h1>
  <p>This is the about page</p>
</main>
</body>
</html>

But if you have another layout, which is located in theme/layouts/home.ejs, in order to use it you would use Front Matter ex.:

---
layout: home
---

# Homepage
This page is using home.ejs layout

body

As you might have noticed from the previous example <%- page.body %> placeholder in the layout ejs is replaced with the actual content, no matter what page is used(markdown, html or ejs) actual content of the page is being rendered and replaces the <%- page.body %> placeholder.

partials

Partials are EJS layout files that can be loaded into the EJS layouts:

<% include partialPath %>
<%- include('partialPath', {key: value}) %>

This can come handy for different layout parts separation and reuse:

<!DOCTYPE html>
<html lang="en-US">
<head>
  <% include partials/head %>
  <% include partials/meta %>
</head>
<body>
  <% include partials/header %>
<main>
  <%- page.body %>
</main>
  <% include partials/footer %>
</body>
</html>

Considering the example above, we could for example create partial that will be reusable accross different layouts, ex, consider partials/head.ejs with content below:

<link rel="stylesheet" type="text/css" href="/css/main.css">
<script src="/js/main.js" defer></script>

this snippet now can be used and loaded in the layout by just adding <% include partials/head %> into the layout.

Front Matter

As was already mentioned Front Matter is not only used for the layout selection, but it's also possible to define page properties which can be accessed from the layouts.

Considering a Front Matter below:

---
title: About page
showSidebar: true
---

Data defined in the Front Matter is accessible from the layout files using page object:

<title><%= page.title %></title>
<meta property="og:title" content="EJS">

...
</head>
<body>
...
<%if (page.showSidebar) { %>
  <% include partials/sidebar %>
<% } %>

Helpers

There are also some built in helpers in CMintS that can be used out of the box:

Helper Type Description
page.pathname Variable URL path of current page (without locale)
page.locale Variable Locale of the current page
page.locales Array Other locales that current page is available in
page.urlLocale Variable Locale as it's specified in the URL.
page.markdown.toc Object Page's Table Of Content
i18n.getPageLocales Function Get available locales for a specific page
i18n.href Function Generate href and hreflang for path. Check if the target path is available in current language otherwise falls back to default language.
site.queryPages Function Query Array of pages metadata Objects.

page.pathname

URL path of current page (without locale):

<a <%-i18n.href(item.url)%> <% if (item.url == page.pathname) { %>class="active"<% } %>>

page.locale

Locale of the current page:

<!DOCTYPE html>
<html lang="<%= page.locale %>">
<head>
  <title>...</title>
</head>
<body>
...

page.locales

Other locales that current page is available in:

<!DOCTYPE html>
<html lang="<%= page.locale %>">
<head>
...
<!-- og:locale, og:locale:alternate, rel="canonical" rel="alternate"  -->
<% for (const locale of page.locales) { %>
  <% const localeRegion = site.localeMap[locale] ? site.localeMap[locale].region : locale; %>
  <% if (locale == page.locale) { %>
    <meta property="og:locale" content="<%= localeRegion %>" />
    <link rel="canonical" href="https://<%= site.domain %>/<%= page.pathname %>">
  <% } else { %>
    <meta property="og:locale:alternate" content="<%= localeRegion %>" />
    <link rel="alternate" href="https://<%= site.domain %>/<%= locale %>/<%= page.pathname %>" hreflang="<%= locale %>" />
  <% } %>
<% } %>
</head>
<body>
...

page.urlLocale

Locale as it's specified in the URL.

  • https://example.com/about
  • https://example.com/en/about
  • https://example.com/de/about
  • https://example.com/es/about

Considering the URL structure above, sometimes you might want to have a redirection script on pages that doesn't have locale specified in the URL, which in the current case is https://example.com/about, this is when current helper comes handy, for example:

<% if (!page.urlLocale) { %>
  <% include redirectionScript %>
<% } %>

page.markdown.toc

page.markdown.toc Object can be used in the ".ejs" to create a Table Of Content. The Object is a tree where each node corresponds to a markdown Heading containing id and title of the heading. If the node contain children, then all children nodes can be accessible by the node's children property:

{
  "children": [
    {
      "id": "ejs",
      "title": "EJS",
      "children": [
        {
          "id": "layout",
          "title": "Layout"
        },
        {
          "id": "body",
          "title": "Body"
        }
        ...

So, in order to construct a Table Of Content from that variable an EJS snippet an .ejs file like the one below can be used:

<%/* partials/navigations/toc.ejs */%>

<% if (items) { %>
  <ul>
    <% items.forEach(function(item){ %>
    <li>
        <% if (item.id) { %>
          <a href="#<%= item.id %>"><%= item.text %></a>
        <% } %>
        <% if (item.children) { %>
          <%- include('toc', {items: item.children}) %>
        <% } %>
    </li>
    <% }) %>
  </ul>
<% } %>

Considering the example above, the TOC can be generated by simply calling the .ejs file and passing the page.markdown.toc to it, for example:

<%if (page.
  <aside id="toc">
    <h2>Table of content</h2>
    <%- include('partials/navigations/toc', {items: page.markdown.toc.children}) %>
  </aside>
<% } %>

i18n.getPageLocales

Get available locales for a specific page

<% const configPageLocales = i18n.getPageLocales("documentation/getting-started/configuration"); %>
<% const homepageLocales = i18n.getPageLocales("index"); %>
<% if (homepageLocales.includes("de")) { %>
  Homepage is available in German
<% } %>
<% if (configPageLocales.includes("es")) { %>
  Config page is available in Spanish
<% } %>

i18n.href

Generate href and hreflang for path. Check if the target path is available in current language otherwise falls back to default language.

<a <%-i18n.href("documentation")%> class="button">{get-started[Main button text] Get started}</a>

site.queryPages

Query Array of pages metadata Objects.

Besides of Front Matter page metadata, pages contain pathname which is actual pathname of the page and originalPathname which holds pathname ignoring permalink.

<% let docPages = site.queryPages((data) => data.categories &&
                                          data.categories.includes("documentation")) %>

<% docPages.forEach(function(item) { %>
  <a <%- i18n.href(item.pathname) %>> <%= item.title %> </a>
<% }); %>