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,list
for list,page
for 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.twig
if thelayout
variable is set to "my-layout" (in the front matter) - if not, with
home.html.twig
if the file exists - if not, with
index.html.twig
if the file exists - if not, with
list.html.twig
if the file exists - etc.
All rules are detailed below, for each page type, in the priority order.
Type homepage
<layout>.<format>.twig
index.<format>.twig
home.<format>.twig
list.<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>.twig
page.<format>.twig
_default/<layout>.<format>.twig
_default/page.<format>.twig
Type section
<layout>.<format>.twig
<section>/index.<format>.twig
<section>/list.<format>.twig
section/<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>.twig
taxonomy/<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.authors
data/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
:true
if 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
dump
function 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.xml
template: list all pages sorted by date. _default/robots.txt.twig
- The
robots.txt
template: 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.