What's on this page
Templates
Cecil is powered by the Twig template engine, so please refer to the official documentation to learn how to use it.
Example
{# this is a template example #}
<h1>{{ page.title }} - {{ site.title }}</h1>
<span>{{ page.date|date('j M Y') }}</span>
<p>{{ page.content }}</p>
<ul>
{% for tag in page.tags %}
<li>{{ tag }}</li>
{% endfor %}
</ul>
{# #}: adds comments{{ }}: outputs content of variables or expressions{% %}: executes statements, like loop (for), condition (if), etc.|filter(): filters or formats content
Files organization
There is two kinds of templates, layouts and others templates: layouts are used to render pages, and each of them can include templates.
Templates files are stored in the layouts/ directory and must be named according to the following convention:
layouts/(<section>/)<type>|<layout>.<format>(.<language>).twig
<section>(optional)- The section of the page (e.g.:
blog). <type>- The page type:
home(orindex) for homepage,listfor list,pagefor page, etc. (See Lookup rules for details). <layout>(optional)- The custom layout name defined in the front matter of the page (e.g.:
layout: my-layout). <language>(optional)- The language of the page (e.g.:
fr). <format>- The output format of the rendered page (e.g.:
html,rss,json,xml, etc.). - ".twig"
- The mandatory Twig file extension.
Examples:
layouts/home.html.twig # `type` is "homepage"
layouts/page.html.twig # `type` is "page"
layouts/page.html.fr.twig # `type` is "page" and `language` is "fr"
layouts/my-layout.html.twig # `layout` is "my-layout"
layouts/blog/list.html.twig # `section` is "blog"
layouts/blog/list.rss.twig # `section` is "blog" and `format` is "rss"
<mywebsite>
├─ ...
├─ layouts <- Layouts and templates
| ├─ my-layout.html.twig
| ├─ index.html.twig <- Used by type "homepage"
| ├─ list.html.twig <- Used by types "homepage", "section" and "term"
| ├─ list.rss.twig <- Used by types "homepage", "section" and "term", for RSS output format
| ├─ page.html.twig <- Used by type "page"
| ├─ ...
| ├─ _default <- Default layouts, that can be easily extended
| | ├─ list.html.twig
| | ├─ page.html.twig
| | └─ ...
| └─ partials
| ├─ footer.html.twig <- Included template
| └─ ...
└─ themes
└─ <theme>
└─ layouts <- Theme layouts and templates
└─ ...
Lookup rules
In most of cases you don’t need to specify the layout: Cecil selects the most appropriate layout, according to the page type.
For example, the HTML output of home page (index.md) will be rendered:
- with
my-layout.html.twigif thelayoutvariable is set to "my-layout" (in the front matter) - if not, with
home.html.twigif the file exists - if not, with
index.html.twigif the file exists - if not, with
list.html.twigif the file exists - etc.
All rules are detailed below, for each page type, in the priority order.
Type homepage
<layout>.<format>.twigindex.<format>.twighome.<format>.twiglist.<format>.twig_default/<layout>.<format>.twig_default/index.<format>.twig_default/home.<format>.twig_default/list.<format>.twig_default/page.<format>.twig
Type page
<section>/<layout>.<format>.twig<layout>.<format>.twig<section>/page.<format>.twigpage.<format>.twig_default/<layout>.<format>.twig_default/page.<format>.twig
Type section
<layout>.<format>.twig<section>/index.<format>.twig<section>/list.<format>.twigsection/<section>.<format>.twig_default/section.<format>.twig_default/list.<format>.twig
Type vocabulary
taxonomy/<plural>.<format>.twig_default/vocabulary.<format>.twig
Type term
taxonomy/<term>.<format>.twigtaxonomy/<singular>.<format>.twig_default/term.<format>.twig_default/list.<format>.twig
Variables
The application passes variables to the templates for manipulation in the template. Variables may have attributes or elements you can access, too.
Use a dot (.) to access attributes of a variable:
{{ foo.bar }}
You can use variables from different scopes: site, page, cecil.
site
The site variable contains all variables from the configuration and built-in variables.
Example:
title: "My amazing website!"
Can be displayed in a template with:
{{ site.title }}
Built-in variables
| Variable | Description |
|---|---|
site.home |
ID of the home page. |
site.pages |
Collection of pages, in the current language. |
site.pages.showable |
Same as site.pages but filtered by "showable" status (published pages and not virtual/redirect/excluded). |
site.page('id') |
A page with the given ID. |
site.allpages |
Collection of all pages, regardless of their language. |
site.taxonomies |
Collection of vocabularies. |
site.time |
Timestamp of the last generation. |
site.debug |
Debug mode: true or false. |
site.menus
Loop on site.menus.<menu> to get each entry of the <menu> collection (e.g.: main).
| Variable | Description |
|---|---|
<entry>.name |
Menu entry name. |
<entry>.url |
Menu entry URL. |
<entry>.weight |
Menu entry weight (useful to sort menu entries). |
Example:
<nav>
<ol>
{% for entry in site.menus.main|sort_by_weight %}
<li><a href="{{ url(entry.url) }}" data-weight="{{ entry.weight }}">{{ entry.name }}</a></li>
{% endfor %}
</ol>
</nav>
site.language
Informations about the current language.
| Variable | Description |
|---|---|
site.language |
Language code (e.g.: en). |
site.language.name |
Language name (e.g.: English). |
site.language.locale |
Language locale code (e.g.: en_EN). |
site.language.weight |
Language position in the languages list. |
site.static
The static files collection can be accessed via site.static if the static load is enabled.
Each file exposes the following properties:
path: relative path (e.g.:/images/img-1.jpg)date: creation date (timestamp)updated: modification date (timestamp)name: name (e.g.:img-1.jpg)basename: name without extension (e.g.:img-1)ext: extension (e.g.:jpg)exif: image EXIF data (array)audio: Mp3Info object
site.data
A data collection can be accessed via site.data.<filename> (without file extension).
Examples:
data/authors.yml:site.data.authorsdata/galleries/gallery-1.json:site.data.galleries.gallery-1
page
Contains built-in variables of a page and those set in the front matter.
| Variable | Description | Example |
|---|---|---|
page.id |
Unique identifier. | blog/post-1 |
page.title |
File name (without extension). | Post 1 |
page.date |
File creation date. | DateTime |
page.updated |
File modification date. | DateTime |
page.body |
File body. | Markdown |
page.content |
File body converted in HTML. | HTML |
page.section |
File root folder (slugified). | blog |
page.path |
File path (slugified). | blog/post-1 |
page.slug |
File name (slugified). | post-1 |
page.tags |
Array of tags. | [Tag 1, Tag 2] |
page.categories |
Array of categories. | [Category 1, Category 2] |
page.pages |
Collection of all sub pages. | Collection |
page.pages.showable |
page.pages with "showable" pages only. |
Collection |
page.type |
homepage, page, section, vocabulary or term. |
page |
page.filepath |
File system path. | Blog/Post 1.md |
page.translations |
Collection of translated pages. | Collection |
page.<prev/next>
Navigation between pages in a same Section.
| Variable | Description | Example |
|---|---|---|
page.prev |
Previous page. | Page |
page.next |
Next page. | Page |
Example:
<a href="{{ url(page.prev) }}">{{ page.prev.title }}</a>
page.paginator
Paginator help you to build a navigation for list's pages: homepage, sections, and taxonomies.
| Variable | Description |
|---|---|
page.paginator.pages |
Pages Collection. |
page.paginator.pages_total |
Number total of pages. |
page.paginator.count |
Number of paginator's pages. |
page.paginator.current |
Position index of the current page. |
page.paginator.links.first |
Page ID of the first page. |
page.paginator.links.prev |
Page ID of the previous page. |
page.paginator.links.self |
Page ID of the current page. |
page.paginator.links.next |
Page ID of the next page. |
page.paginator.links.last |
Page ID of the last page. |
page.paginator.links.path |
Page ID without the position index. |
Example:
{% if page.paginator %}
<div>
{% if page.paginator.links.prev is defined %}
<a href="{{ url(page.paginator.links.prev) }}">Previous</a>
{% endif %}
{% if page.paginator.links.next is defined %}
<a href="{{ url(page.paginator.links.next) }}">Next</a>
{% endif %}
</div>
{% endif %}
Example:
{% if page.paginator %}
<div>
{% for paginator_index in 1..page.paginator.count %}
{% if paginator_index != page.paginator.current %}
{% if paginator_index == 1 %}
<a href="{{ url(page.paginator.links.first) }}">{{ paginator_index }}</a>
{% else %}
<a href="{{ url(page.paginator.links.path ~ '/' ~ paginator_index) }}">{{ paginator_index }}</a>
{% endif %}
{% else %}
{{ paginator_index }}
{% endif %}
{% endfor %}
</div>
{% endif %}
Taxonomy
Variables available in vocabulary and term templates.
Vocabulary
| Variable | Description |
|---|---|
page.plural |
Vocabulary name in plural form. |
page.singular |
Vocabulary name in singular form. |
page.terms |
List of terms (Collection). |
Term
| Variable | Description |
|---|---|
page.term |
Term ID. |
page.pages |
List of pages in this term (Collection). |
cecil
| Variable | Description |
|---|---|
cecil.url |
URL of the official website. |
cecil.version |
Cecil current version. |
cecil.poweredby |
Print Cecil v%s with %s is the current version. |
Functions
Functions can be called to generate content. Functions are called by their name followed by parentheses (
()) and may have arguments.
url
Creates a valid URL for a page, an asset, a page ID or a path.
{{ url(value, {options}) }}
| Option | Description | Type | Default |
|---|---|---|---|
| canonical | Prefixes path with baseurl or use canonical.url. |
boolean | false |
| format | Defines page output format (e.g.: json). |
string | html |
| language | Trying to force page language (e.g.: fr). |
string | null |
Examples:
# page
{{ url(page) }}
{{ url(page, {canonical: true}) }}
{{ url(page, {format: json}) }}
{{ url(page, {language: fr}) }}
# asset
{{ url(asset('styles.css')) }}
# page ID
{{ url('page-id') }}
# path
{{ url(menu.url) }}
{{ url('tags/' ~ tag) }}
asset
Creates an asset (CSS, JavaScript, image, audio, etc.) from a file path, an URL or an array of files path (bundle).
{{ asset(path, {options}) }}
| Option | Description | Type | Default |
|---|---|---|---|
| fingerprint | Add the file content finger print to the file name. | boolean | true |
| minify | Compress file content (CSS or JavaScript). | boolean | true |
| filename | File where to save content. | string | styles.css or scripts.js |
| ignore_missing | Do not stop build if file don't exists. | boolean | false |
| remote_fallback | Load a local asset if the remote one don't exists. | string | null |
Examples:
# CSS
{{ asset('styles.css') }}
# CSS bundle
{{ asset(['poole.css', 'hyde.css'], {filename: styles.css}) }}
# JavaScript
{{ asset('scripts.js') }}
# image
{{ asset('image.jpeg') }}
# remote file
{{ asset('https://cdnjs.cloudflare.com/ajax/libs/anchor-js/4.3.1/anchor.min.js', {minify: false}) }}
# with filter
{{ asset('styles.css')|minify }}
{{ asset('styles.scss')|to_css|minify }}
Asset attributes
Assets created with the asset() function expose some useful attributes:
file: filesystem pathfiles: array of filesystem path in case of bundlefilename: file namepath_source: relative path before processingpath: relative pathmissing:trueif file not found, but missing is ollowedext: extensiontype: media type (e.g.:image)subtype: media sub type (e.g.:image/jpeg)size: size in octetscontent_source: content before processingcontent: file contentintegrity: integrity hashwidth: image widthheight: image heightexif: image EXIF data as arrayaudio: Mp3Info objectvideo: array of video dimensions (width and height)
Examples:
# image width in pixels
{{ asset('image.png').width }}px
# photo's date in seconds
{{ asset('photo.jpeg').exif.DateTimeOriginal|date('U') }}
# MP3 song duration in minutes
{{ asset('title.mp3').audio.duration|round }} min
# file integrity hash
{% set integrity = asset('styles.scss').integrity %}
image_srcset
Builds the HTML img srcset (responsive) attribute of an image Asset.
{{ image_srcset(asset) }}
Examples:
{% set asset = asset(image_path) %}
<img src="{{ url(asset) }}" width="{{ asset.width }}" height="{{ asset.height }}" alt="" class="asset" srcset="{{ image_srcset(asset) }}" sizes="{{ image_sizes('asset') }}">
image_sizes
Returns the HTML img sizes attribute based on a CSS class name.
It should be use in conjunction with the image_srcset function.
{{ image_sizes('class') }}
Examples:
{% set asset = asset(image_path) %}
<img src="{{ url(asset) }}" width="{{ asset.width }}" height="{{ asset.height }}" alt="" class="asset" srcset="{{ image_srcset(asset) }}" sizes="{{ image_sizes('asset') }}">
integrity
Creates the hash (sha384) of a file (from an asset or a path).
{{ integrity(asset) }}
Used for SRI (Subresource Integrity).
Example:
{{ integrity('styles.css') }}
readtime
Determines read time of a text, in minutes.
{{ readtime(text) }}
Example:
{{ readtime(page.content) }} min
getenv
Gets the value of an environment variable from its key.
{{ getenv(var) }}
Example:
{{ getenv('VAR') }}
dump
The
dumpfunction dumps information about a template variable. This is mostly useful to debug a template that does not behave as expected by introspecting its variables:
{{ dump(user) }}
d
The d() function is the HTML version of dump() and use the Symfony VarDumper Component behind the scenes.
{{ d(variable, {theme: light}) }}
- If variable is not provided then the function returns the current Twig context
- Available themes are « light » (default) and « dark »
Sorts
Sorting collections (of pages, menus or taxonomies).
sort_by_title
Sorts a collection by title (with natural sort).
{{ <collection>|sort_by_title }}
Example:
{{ site.pages|sort_by_title }}
sort_by_date
Sorts a collection by date (most recent first).
{{ <collection>|sort_by_date(variable='date', desc_title=false) }}
Example:
# sort by date
{{ site.pages|sort_by_date }}
# sort by updated variable instead of date
{{ site.pages|sort_by_date(variable='updated') }}
# sort items with the same date by desc title
{{ site.pages|sort_by_date(desc_title=true) }}
# reverse sort
{{ site.pages|sort_by_date|reverse }}
sort_by_weight
Sorts a collection by weight (lighter first).
{{ <collection>|sort_by_weight }}
Example:
{{ site.menus.main|sort_by_weight }}
sort
For more complex cases, you should use Twig’s native sort.
Example:
{% set files = site.static|sort((a, b) => a.date|date('U') < b.date|date('U')) %}
Filters
Variables can be modified by filters. Filters are separated from the variable by a pipe symbol (
|). Multiple filters can be chained. The output of one filter is applied to the next.
{{ page.title|truncate(25)|capitalize }}
filter_by
Filters a pages collection by variable name/value.
{{ <collection>|filter_by(variable, value) }}
Example:
{{ pages|filter_by('section', 'blog') }}
filter
For more complex cases, you should use Twig’s native filter.
Example:
{% pages|filter(p => p.virtual == false and p.id not in ['page-1', 'page-2']) %}
markdown_to_html
Converts a Markdown string to HTML.
{{ markdown|markdown_to_html }}
{% apply markdown_to_html %}
{# Markdown here #}
{% endapply %}
Examples:
{% set markdown = '**This is bold text**' %}
{{ markdown|markdown_to_html }}
{% apply markdown_to_html %}
**This is bold text**
{% endapply %}
toc
Extracts table of content from a Markdown string, in the given format ("html" or "json", "html" by default) and an optional base URL.
{{ markdown|toc(format, url) }}
Examples:
{{ page.body|toc }}
{{ page.body|toc('json') }}
{{ page.body|toc(url=url(page)) }}
json_decode
Converts a JSON string to an array.
{{ json|json_decode }}
Example:
{% set json = '{"foo": "bar"}' %}
{% set array = json|json_decode %}
{{ array.foo }}
yaml_parse
Converts a YAML string to an array.
{{ yaml|yaml_parse }}
Example:
{% set yaml = 'key: value' %}
{% set array = yaml|yaml_parse %}
{{ array.key }}
slugify
Converts a string to a slug.
{{ string|slugify }}
excerpt
Truncates a string and appends suffix.
{{ string|excerpt(integer, suffix) }}
| Option | Description | Type | Default |
|---|---|---|---|
| length | Truncates after this number of characters. | integer | 450 |
| suffix | Appends characters. | string | … |
Examples:
{{ variable|excerpt }}
{{ variable|excerpt(250, '...') }}
excerpt_html
Reads characters before or after <!-- excerpt --> or <!-- break --> tag.
See Content documentation for details.
{{ string|excerpt_html({separator, capture}) }}
| Option | Description | Type | Default |
|---|---|---|---|
| separator | String to use as separator. | string | excerpt|break |
| capture | Part to capture, before or after the separator. |
string | before |
Examples:
{{ variable|excerpt_html }}
{{ variable|excerpt_html({separator: 'excerpt|break', capture: 'before'}) }}
{{ variable|excerpt_html({capture: 'after'}) }}
to_css
Compiles a Sass file to CSS.
{{ asset(path)|to_css }}
{{ path|to_css }}
Examples:
{{ asset('styles.scss')|to_css }}
fingerprint
Add the file content finger print to the file name.
{{ asset(path)|fingerprint }}
{{ path|fingerprint }}
Examples:
{{ asset('styles.css')|fingerprint }}
minify
Minifying a CSS or a JavaScript file.
{{ asset(path)|minify }}
Examples:
{{ asset('styles.css')|minify }}
{{ asset('scripts.js')|minify }}
minify_css
Minifying a CSS string.
{{ variable|minify_css }}
{% apply minify_css %}
{# CSS here #}
{% endapply %}
Examples:
{% set styles = 'some CSS here' %}
{{ styles|minify_css }}
<style>
{% apply minify_css %}
html {
background-color: #fcfcfc;
color: #444;
}
{% endapply %}
</style>
minify_js
Minifying a JavaScript string.
{{ variable|minify_js }}
{% apply minify_js %}
{# JavaScript here #}
{% endapply %}
Examples:
{% set script = 'some JavaScript here' %}
{{ script|minify_js }}
<script>
{% apply minify_js %}
var test = 'test';
console.log(test);
{% endapply %}
</script>
scss_to_css
Compiles a Sass string to CSS.
{{ variable|scss_to_css }}
{% apply scss_to_css %}
{# SCSS here #}
{% endapply %}
Alias: sass_to_css.
Examples:
{% set scss = 'some SCSS here' %}
{{ scss|scss_to_css }}
<style>
{% apply scss_to_css %}
$color: #fcfcfc;
div {
color: lighten($color, 20%);
}
{% endapply %}
</style>
resize
Resizes an image to a specified with.
{{ asset(image_path)|resize(integer) }}
Example:
{{ asset(page.image)|resize(300) }}
webp
Converts an image to WebP format.
Example:
<picture>
<source type="image/webp" srcset="{{ asset(image_path)|webp }}">
<img src="{{ url(asset(image_path)) }}" width="{{ asset(image_path).width }}" height="{{ asset(image_path).height }}" alt="">
</picture>
dataurl
Returns the data URL of an asset.
{{ asset(path)|dataurl }}
{{ asset(image_path)|dataurl }}
lqip
Returns a Low Quality Image Placeholder (100x100 px, 50% blurred) as data URL.
{{ asset(image_path)|lqip }}
dominant_color
Returns the dominant hexadecimal color of an image.
{{ asset(image_path)|dominant_color }}
# #F2D07F
inline
Outputs the content of an Asset.
{{ asset(path)|inline }}
Example:
{{ asset('styles.css')|inline }}
html
Converts an asset into an HTML element.
{{ asset(path)|html({attributes, options}) }}
| Option | Description | Type | Default |
|---|---|---|---|
| attributes | Adds name="value" couple to the HTML element. |
array | |
| options | {responsive: true}: creates responsives images.{webp: true}: creates WebP versions of the image.{preload: true}: preloads CSS. |
array |
Examples:
{{ asset('image.png')|html }}
{{ asset('image.jpg')|html({alt: 'Description', loading: 'lazy'}, {responsive: true, webp: true}) }}
{{ asset('styles.css')|html({media: 'print'}) }}
{{ asset('styles.css')|html({title: 'Main theme'}, {preload: true}) }}
preg_split
Splits a string into an array using a regular expression.
{{ string|preg_split(pattern, limit) }}
Example:
{% set headers = page.content|preg_split('/<h3[^>]*>/') %}
preg_match_all
Performs a regular expression match and return the group for all matches.
{{ string|preg_match_all(pattern, group) }}
Example:
{% set tags = page.content|preg_match_all('/<[^>]+>(.*)<\\/[^>]+>/') %}
hex_to_rgb
Converts a hexadecimal color to RGB.
{{ color|hex_to_rgb }}
Localization
Cecil support text translation and date localization.
Text translation
Uses the trans tag or filter to translate texts in templates.
{% trans with variables into locale %}{% endtrans %}
{{ message|trans(variables = []) }}
Examples
{% trans %}Hello World!{% endtrans %}
{{ message|trans }}
Include variables:
{% trans with {'%name%': 'Arnaud'} %}Hello %name%!{% endtrans %}
{{ message|trans({'%name%': 'Arnaud'}) }}
Force locale:
{% trans into 'fr_FR' %}Hello World!{% endtrans %}
Pluralize:
{% trans with {'%count%': 42}%}{0}I don't have apples|{1}I have one apple|]1,Inf[I have %count% apples{% endtrans %}
Translation files
Translation files must be named messages.<locale>.<format> and stored in the translations directory.
Cecil supports yaml and mo (Gettext) file formats by default.
Example:
<mywebsite>
└─ translations
├─ messages.fr_FR.mo <- Machine Object format
└─ messages.fr_FR.yaml <- Yaml format
Date localization
Uses the Twig format_date filter to localize a date in templates.
{{ page.date|format_date('long') }}
# September 30, 2022
Supported values are: short, medium, long, and full.
Built-in templates
Cecil comes with a set of built-in templates.
Default templates
_default/page.html.twig- A simple main template with a clean CSS.
_default/list.html.twig- A pages list with (an optional) pagination.
_default/list.atom.twig- An Atom feed.
_default/list.rss.twig- A RSS feed.
_default/vocabulary.html.twig- A simple list of all terms of a vocabulary.
_default/sitemap.xml.twig- The
sitemap.xmltemplate: list all pages sorted by date. _default/robots.txt.twig- The
robots.txttemplate: allow all pages except 404, with a reference to the XML sitemap. _default/404.html.twig- A basic error 404 ("Page not found") template.
_default/redirect.html.twig- The redirect template.
Partial templates
partials/navigation.html.twig- A main menu navigation.
partials/paginator.html.twig- A simple paginated navigation for list templates with "Previous" and "Next" links.
partials/metatags.html.twig- All metatags in one template: title, description, canonical, open-graph, twitter card, etc. See configuration.
partials/languages.html.twig- A basic switcher between languages.