<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://cecil.app/xsl/atom.xsl" media="all"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://cecil.app/documentation/</id>
  <title>Cecil - Documentation</title>
  <subtitle><![CDATA[Cecil is a command-line PHP application that merges Markdown pages, medias and Twig templates to generate a static website.]]></subtitle>
  <link href="https://cecil.app/documentation/atom.xml" rel="self" type="application/atom+xml" />
  <link href="https://cecil.app/documentation/" rel="alternate" type="text/html" />
  <updated>2026-10-07T21:40:48+00:00</updated>
  <author>
    <name>Cecil</name>
    <uri>https://cecil.app</uri>
  </author>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/getting-started/installation/</id>
    <title>Installation</title>
    <published>2026-10-05T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/getting-started/installation/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Installation</h1>
<p>Cecil is distributed as a single executable file, <code translate="no">cecil.phar</code>, which runs anywhere PHP is installed.</p>
<h2 id="requirements">Requirements</h2>
<ul>
<li><a href="https://www.php.net/manual/install.php" target="_blank" rel="noopener noreferrer">PHP</a> 8.3+</li>
<li>PHP extensions: <code translate="no">fileinfo</code>, <code translate="no">gd</code> and <code translate="no">mbstring</code></li>
</ul>
<p>Check your PHP version and loaded extensions:</p>
<pre><code class="language-bash hljs bash" translate="no">php -v
php -m</code></pre>
<h3 id="optional-extensions">Optional extensions</h3>
<table>
<thead>
<tr>
<th>Extension</th>
<th>Usage</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.php.net/manual/book.intl.php" target="_blank" rel="noopener noreferrer"><code translate="no">intl</code></a></td>
<td>Dates <a href="/templates/localization/">localization</a> with other locales than <code translate="no">en</code> (improves performance otherwise).</td>
</tr>
<tr>
<td><a href="https://www.php.net/manual/book.imagick.php" target="_blank" rel="noopener noreferrer"><code translate="no">imagick</code></a></td>
<td>Image processing, preferred over GD when available.</td>
</tr>
<tr>
<td><a href="https://www.php.net/manual/book.ffi.php" target="_blank" rel="noopener noreferrer"><code translate="no">ffi</code></a></td>
<td>Image processing with <a href="https://www.libvips.org/" target="_blank" rel="noopener noreferrer">libvips</a> (requires <a href="#composer">Composer installation</a>).</td>
</tr>
</tbody>
</table>
<h2 id="download-the-phar">Download the PHAR</h2>
<p>Download <code translate="no">cecil.phar</code> from your terminal:</p>
<pre><code class="language-bash hljs bash" translate="no">curl -LO https://cecil.app/cecil.phar</code></pre>
<p>Then run it with PHP:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar --version</code></pre>
<aside class="note note-info"><p>You can also download a <a href="/download/">specific version or the preview version</a>.</p></aside>
<h2 id="install-globally">Install globally</h2>
<p>Installing Cecil globally allows you to run the <code translate="no">cecil</code> command from any directory, instead of <code translate="no">php cecil.phar</code>.</p>
<h3 id="macos-and-linux">macOS and Linux</h3>
<p>With <a href="https://brew.sh" target="_blank" rel="noopener noreferrer">Homebrew</a>:</p>
<pre><code class="language-bash hljs bash" translate="no">brew install cecilapp/tap/cecil</code></pre>
<p>Or manually, by moving the PHAR in a directory of your <code translate="no">PATH</code>:</p>
<pre><code class="language-bash hljs bash" translate="no">mv cecil.phar /usr/<span class="hljs-built_in">local</span>/bin/cecil
chmod +x /usr/<span class="hljs-built_in">local</span>/bin/cecil</code></pre>
<h3 id="windows">Windows</h3>
<p>With <a href="https://scoop.sh" target="_blank" rel="noopener noreferrer">Scoop</a>:</p>
<pre><code class="language-bash hljs bash" translate="no">scoop install https://cecil.app/scoop/cecil.json</code></pre>
<p>Or manually:</p>
<ol>
<li>Move <code translate="no">cecil.phar</code> in a dedicated directory, like <code translate="no">C:\bin</code></li>
<li>Rename it from <code translate="no">cecil.phar</code> to <code translate="no">cecil</code></li>
<li>Append <code translate="no">;C:\bin</code> to your <code translate="no">PATH</code> environment variable</li>
<li>Create a <a href="https://raw.githubusercontent.com/Cecilapp/Cecil/main/bin/cecil.bat" target="_blank" rel="noopener noreferrer">wrapping batch script</a> next to it</li>
</ol>
<h3 id="phive">PHIVE</h3>
<p>With <a href="https://phar.io" target="_blank" rel="noopener noreferrer">PHIVE</a> (The PHAR Installation and Verification Environment):</p>
<pre><code class="language-bash hljs bash" translate="no">phive install cecil</code></pre>
<h3 id="composer">Composer</h3>
<p>With <a href="https://getcomposer.org" target="_blank" rel="noopener noreferrer">Composer</a>:</p>
<pre><code class="language-bash hljs bash" translate="no">composer global require cecil/cecil</code></pre>
<aside class="note note-important"><p>Make sure Composer's global binaries directory is in your <code translate="no">PATH</code>. Run <code translate="no">composer global config bin-dir --absolute</code> to get it.</p></aside>
<aside class="note note-tip"><p>To use Cecil as a dependency of a PHP project, see <a href="/developers/library/">Library</a>.</p></aside>
<h2 id="verify-the-installation">Verify the installation</h2>
<pre><code class="language-bash hljs bash" translate="no">cecil --version</code></pre>
<aside class="note note-info"><p>Run <code translate="no">cecil list</code> to display the <a href="/commands/">available commands</a>.</p></aside>
<h2 id="update">Update</h2>
<p>Update Cecil to the latest version according to your installation method:</p>
<pre><code class="language-bash hljs bash" translate="no"><span class="hljs-comment"># PHAR</span>
php cecil.phar self-update
<span class="hljs-comment"># Homebrew</span>
brew upgrade cecilapp/tap/cecil
<span class="hljs-comment"># Scoop</span>
scoop update cecil
<span class="hljs-comment"># PHIVE</span>
phive update cecil
<span class="hljs-comment"># Composer</span>
composer global update cecil/cecil</code></pre>
<p>The <code translate="no">self-update</code> command (PHAR only) also accepts the following options:</p>
<ul>
<li><code translate="no">--rollback</code>: reverts to the previous installed version</li>
<li><code translate="no">--stable</code>: forces an update to the last stable version</li>
<li><code translate="no">--preview</code>: forces an update to the last unstable version</li>
</ul>
<aside class="note note-info"><p>The changelog of each release is available on <a href="https://github.com/Cecilapp/Cecil/releases" target="_blank" rel="noopener noreferrer">GitHub</a>.</p></aside>
<h2 id="troubleshooting">Troubleshooting</h2>
<h3 id="command-not-found-cecil"><code translate="no">command not found: cecil</code></h3>
<p>The installation directory is not in your <code translate="no">PATH</code>: add it, or run Cecil with <code translate="no">php cecil.phar</code> from the directory containing the PHAR.</p>
<h3 id="php-version-or-extension-error">PHP version or extension error</h3>
<p>Your PHP CLI may differ from the one you expect (several versions installed): check it with <code translate="no">php -v</code> and <code translate="no">php --ini</code>, then enable missing extensions in the loaded <code translate="no">php.ini</code> file.</p>
<h3 id="diagnose-a-website">Diagnose a website</h3>
<p>Once a website is created, run the <a href="/commands/doctor/"><code translate="no">doctor</code></a> command to diagnose its configuration.</p>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/developers/architecture/</id>
    <title>Architecture</title>
    <published>2026-05-27T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/developers/architecture/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Architecture</h1>
<h2 id="diagram">Diagram</h2>
<pre><code class="language-mermaid" translate="no">graph TD
    %% Entry
    CLI["bin/cecil\n(CLI entry point)"]
    APP["Application\n(Symfony Console)"]
    CMD["Commands\nbuild / serve / new:site\nnew:page / clear / show:content"]

    CLI --&gt; APP --&gt; CMD

    %% Orchestrator
    BUILDER["Builder\n(Orchestrator)"]
    CONFIG["Configuration\n(config.yml + default.php\n+ themes)"]
    CMD --&gt; BUILDER
    CONFIG --&gt; BUILDER

    %% Pipeline
    subgraph PIPELINE["Build pipeline (steps)"]
        S1["1. Pages/Load\n(Finder -&gt; Markdown files)"]
        S2["2. Data/Load\n(YAML files)"]
        S3["3. StaticFiles/Load\n(load static files)"]
        S4["4. Pages/Create\n(create Page objects)"]
        S5["5. Pages/Convert\n(Markdown -&gt; HTML\nfront matter)"]
        S6["6. Taxonomies/Create\n(create taxonomies)"]
        S7["7. Pages/Generate\n(generators)"]
        S8["8. Menus/Create\n(create menus)"]
        S9["9. StaticFiles/Copy\n(copy static files)"]
        S10["10. Pages/Render\n(Twig rendering)"]
        S11["11. Pages/Save\n(save pages)"]
        S12["12. Assets/Save\n(save assets)"]
        S13["13. Optimize/*\n(optimize HTML/CSS/JS/Images)"]

        S1 --&gt; S2 --&gt; S3 --&gt; S4 --&gt; S5 --&gt; S6 --&gt; S7 --&gt; S8 --&gt; S9 --&gt; S10 --&gt; S11 --&gt; S12 --&gt; S13
    end

    BUILDER --&gt; PIPELINE

    %% Subsystems
    subgraph COLLECTIONS["Collections"]
        PC["PagesCollection\n(Page objects)"]
        TC["TaxonomiesCollection\n(vocabularies/terms)"]
        MC["MenusCollection"]
    end

    subgraph GENERATORS["Generators (virtual pages)"]
        GP["Pagination"]
        GT["Taxonomy"]
        GS["Section"]
        GR["Redirect"]
        GD["DefaultPages (home, 404)"]
    end

    subgraph RENDERER["Twig rendering"]
        TW["Twig engine"]
        EXT["Extensions\n(Core, Content, Collection)"]
        PP["PostProcessors\n(metadata, excerpts, links)"]
        TH["Themes / Layouts"]
        TW --&gt; EXT
        TW --&gt; PP
        TH --&gt; TW
    end

    subgraph ASSETS["Assets"]
        AL["Asset locator"]
        AC["Compiler\n(SCSS -&gt; CSS)"]
        AI["Image processor\n(responsive, WebP, AVIF)"]
        AO["Optimizer\n(CSS/JS minification)"]
    end

    subgraph OUTPUT["Output (_site/)"]
        HTML[".html pages"]
        CSS2["CSS/JS assets"]
        IMG["images"]
        SF["static files"]
    end

    S4 --&gt; PC
    S6 --&gt; TC
    S8 --&gt; MC
    S7 --&gt; GENERATORS
    GENERATORS --&gt; PC
    S10 --&gt; RENDERER
    S12 --&gt; ASSETS

    PC --&gt; RENDERER
    TC --&gt; RENDERER
    MC --&gt; RENDERER

    RENDERER --&gt; S11
    S11 --&gt; HTML
    S12 --&gt; CSS2
    S12 --&gt; IMG
    S9 --&gt; SF

    %% Inputs
    subgraph INPUT["Sources"]
        MD["content/\n(Markdown + front matter)"]
        DATA["data/\n(YAML)"]
        STATIC["static/"]
        LAYOUTS["layouts/\n(Twig templates)"]
    end

    MD --&gt; S1
    DATA --&gt; S2
    STATIC --&gt; S3
    LAYOUTS --&gt; TH</code></pre>
<h2 id="key-components-legend">Key Components Legend</h2>
<table>
<thead>
<tr>
<th>Component</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Builder</strong></td>
<td>Central orchestrator that executes steps in sequence</td>
</tr>
<tr>
<td><strong>Config</strong></td>
<td>Merges default + theme + project + CLI configuration</td>
</tr>
<tr>
<td><strong>Steps</strong></td>
<td>Modular pipeline (13 steps), each with <code translate="no">init()</code> / <code translate="no">canProcess()</code> / <code translate="no">process()</code></td>
</tr>
<tr>
<td><strong>Collections</strong></td>
<td>Pages, Taxonomies, Menus: core data structures</td>
</tr>
<tr>
<td><strong>Generators</strong></td>
<td>Create virtual pages (pagination, tags, redirects, etc.)</td>
</tr>
<tr>
<td><strong>Renderer (Twig)</strong></td>
<td>Applies templates + extensions + post-processors</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>Compiles SCSS, optimizes images, fingerprints files</td>
</tr>
</tbody>
</table>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/developers/library/</id>
    <title>Library</title>
    <published>2023-12-13T00:00:00+00:00</published>
    <updated>2026-10-03T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/developers/library/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Library</h1>
<p>Cecil provides a simple PHP API to build your website.</p>
<p>You can read the <a href="https://cecil.app/documentation/library/api/namespaces/cecil.html">API documentation</a> for more details.</p>
<h2 id="installation">Installation</h2>
<pre><code class="language-bash hljs bash" translate="no">composer require cecil/cecil</code></pre>
<h3 id="libvips-support">libvips support</h3>
<p>To process images with <a href="https://www.libvips.org/" target="_blank" rel="noopener noreferrer">libvips</a> (optional), install the libvips driver in your project:</p>
<pre><code class="language-bash hljs bash" translate="no">composer require intervention/image-driver-vips</code></pre>
<aside class="note note-important"><p>This driver requires <a href="https://www.libvips.org/install.html" target="_blank" rel="noopener noreferrer">libvips</a> installed on your system and the PHP <a href="https://www.php.net/manual/book.ffi.php" target="_blank" rel="noopener noreferrer">FFI</a> extension enabled.<br>
Without it, Cecil uses <a href="https://www.php.net/manual/book.imagick.php" target="_blank" rel="noopener noreferrer">Imagick</a> or <a href="https://www.php.net/manual/book.image.php" target="_blank" rel="noopener noreferrer">GD</a> instead.</p></aside>
<h2 id="usage">Usage</h2>
<h3 id="build">Build</h3>
<p>Build a new website with a custom configuration:</p>
<pre><code class="language-php hljs php" translate="no"><span class="hljs-keyword">require_once</span> <span class="hljs-string">'vendor/autoload.php'</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Builder</span>;

$config = [
    <span class="hljs-string">'title'</span>   =&gt; <span class="hljs-string">"My website"</span>,
    <span class="hljs-string">'baseurl'</span> =&gt; <span class="hljs-string">'https://domain.tld/'</span>,
];

Builder::create($config)-&gt;build();

exec(<span class="hljs-string">'php -S localhost:8000 -t _site'</span>); <span class="hljs-comment">// preview locally</span></code></pre>
<aside class="note note-info"><p>The main parameter of the <code translate="no">create</code> method should be a PHP <code translate="no">array</code> or a <a href="https://github.com/Cecilapp/Cecil/blob/main/src/Config.php" target="_blank" rel="noopener noreferrer"><code translate="no">Cecil\Config</code></a> instance.</p></aside>
<h3 id="diagnostic">Diagnostic</h3>
<p>You can also run doctor checks through dedicated domain services, without using CLI commands.</p>
<pre><code class="language-php hljs php" translate="no"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">require_once</span> <span class="hljs-string">'vendor/autoload.php'</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Builder</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Doctor</span>\<span class="hljs-title">SeoDoctor</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Doctor</span>\<span class="hljs-title">SiteDoctor</span>;

$builder = Builder::create(<span class="hljs-keyword">require</span> <span class="hljs-string">'config.php'</span>)
    -&gt;setSourceDir(<span class="hljs-keyword">__DIR__</span>)
    -&gt;setDestinationDir(<span class="hljs-keyword">__DIR__</span>);

$siteDoctor = <span class="hljs-keyword">new</span> SiteDoctor();
$diagnosis = $siteDoctor-&gt;diagnose($builder, <span class="hljs-keyword">__DIR__</span>, [<span class="hljs-string">'cecil.yml'</span>]);

$seoDoctor = <span class="hljs-keyword">new</span> SeoDoctor();
$seoAudit = $seoDoctor-&gt;audit($builder, [
    <span class="hljs-string">'page'</span> =&gt; <span class="hljs-string">''</span>,
    <span class="hljs-string">'include_virtual'</span> =&gt; <span class="hljs-keyword">false</span>,
]);

var_dump($diagnosis[<span class="hljs-string">'errors'</span>], $seoAudit[<span class="hljs-string">'summary'</span>]);</code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/assets/cdn-providers/</id>
    <title>CDN providers</title>
    <published>2023-10-23T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/assets/cdn-providers/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>CDN providers</h1>
<p>Examples of CDN providers <a href="/configuration/assets/#assets-images-cdn"><code translate="no">configuration</code></a>.</p>
<h2 id="cloudinary">Cloudinary</h2>
<p><a href="https://cloudinary.com" target="_blank" rel="noopener noreferrer">https://cloudinary.com</a></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">cdn:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">account:</span> <span class="hljs-string">'xxxx'</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">'https://res.cloudinary.com/%account%/image/fetch/c_limit,w_%width%,q_%quality%,f_%format%,d_default/%image_url%'</span></code></pre>
<h2 id="cloudimage">Cloudimage</h2>
<p><a href="https://www.cloudimage.io" target="_blank" rel="noopener noreferrer">https://www.cloudimage.io</a></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">cdn:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">account:</span> <span class="hljs-string">'xxxx'</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">'https://%account%.cloudimg.io/%image_url%?w=%width%&amp;q=%quality%&amp;force_format=%format%'</span></code></pre>
<h2 id="twicpics">TwicPics</h2>
<p><a href="https://www.twicpics.com" target="_blank" rel="noopener noreferrer">https://www.twicpics.com</a></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">cdn:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">account:</span> <span class="hljs-string">'xxxx'</span>
      <span class="hljs-attr">canonical:</span> <span class="hljs-literal">false</span>
      <span class="hljs-attr">remote:</span> <span class="hljs-literal">false</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">'https://%account%.twic.pics/%image_url%?twic=v1/resize=%width%/quality=%quality%/output=%format%'</span></code></pre>
<p><code translate="no">Source URL</code>: Your website <code translate="no">baseurl</code>.</p>
<h2 id="imgix">imgix</h2>
<p><a href="https://imgix.com" target="_blank" rel="noopener noreferrer">https://imgix.com</a></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">cdn:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">account:</span> <span class="hljs-string">'xxxx'</span>
      <span class="hljs-attr">canonical:</span> <span class="hljs-literal">false</span>
      <span class="hljs-attr">remote:</span> <span class="hljs-literal">false</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">'https://%account%.imgix.net/%image_url%?w=%width%&amp;q=%quality%&amp;fm=%format%'</span></code></pre>
<p><code translate="no">Base URL</code>: Your website <code translate="no">baseurl</code>.</p>
<h2 id="netlify-image-cdn">Netlify Image CDN</h2>
<p><a href="https://docs.netlify.com/image-cdn/overview/" target="_blank" rel="noopener noreferrer">https://docs.netlify.com/image-cdn/overview/</a></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">cdn:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">canonical:</span> <span class="hljs-literal">false</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">'/.netlify/images?url=%image_url%&amp;w=%width%&amp;fm=%format%'</span></code></pre>
<h3 id="run-locally">Run locally</h3>
<h4>Setup Netlify CLI</h4>
<pre><code class="language-bash hljs bash" translate="no">npm install netlify-cli -g
netlify link</code></pre>
<p><code translate="no">netlify.toml</code>:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-string">[dev]</span>
  <span class="hljs-string">targetPort</span> <span class="hljs-string">=</span> <span class="hljs-number">8000</span></code></pre>
<h4>Run local server</h4>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar serve &amp; netlify dev</code></pre>
<p>Open <a href="http://localhost:8888" target="_blank" rel="noopener noreferrer">http://localhost:8888</a></p>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/developers/extend/</id>
    <title>Extend</title>
    <published>2023-04-17T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/developers/extend/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Extend</h1>
<p>Because Cecil is powered by PHP it's easy to extend its capabilities.</p>
<h2 id="pages-generator">Pages Generator</h2>
<p>A generator helps you create pages without Markdown files (for example, with data from an API or a database) or alter existing pages.</p>
<p>Just create a new PHP class in the <code translate="no">Cecil\Generator</code> namespace and add the class name to the <a href="/configuration/pages/#pages-generators"><code translate="no">pages.generators</code></a> list.</p>
<p><strong>Example:</strong></p>
<p><em>/extensions/Cecil/Generator/DummyPage.php</em></p>
<pre><code class="language-php hljs php" translate="no"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Generator</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Collection</span>\<span class="hljs-title">Page</span>\<span class="hljs-title">Page</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Collection</span>\<span class="hljs-title">Page</span>\<span class="hljs-title">Type</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DummyPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractGenerator</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">GeneratorInterface</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generate</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-comment">// create a new page $page, then add it to the site collection</span>
        $page = (<span class="hljs-keyword">new</span> Page(<span class="hljs-string">'my-page'</span>))
            -&gt;setType(Type::PAGE-&gt;value)
            -&gt;setPath(<span class="hljs-string">'mypage'</span>)
            -&gt;setBodyHtml(<span class="hljs-string">'&lt;p&gt;My page body&lt;/p&gt;'</span>)
            -&gt;setVariable(<span class="hljs-string">'language'</span>, <span class="hljs-string">'en'</span>)
            -&gt;setVariable(<span class="hljs-string">'title'</span>, <span class="hljs-string">'My page'</span>)
            -&gt;setVariable(<span class="hljs-string">'date'</span>, now())
            -&gt;setVariable(<span class="hljs-string">'menu'</span>, [<span class="hljs-string">'main'</span> =&gt; [<span class="hljs-string">'weight'</span> =&gt; <span class="hljs-number">99</span>]]);
        <span class="hljs-keyword">$this</span>-&gt;generatedPages-&gt;add($page);
    }
}</code></pre>
<p><em>/extensions/Cecil/Generator/Database.php</em></p>
<pre><code class="language-php hljs php" translate="no"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Generator</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Collection</span>\<span class="hljs-title">Page</span>\<span class="hljs-title">Page</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Collection</span>\<span class="hljs-title">Page</span>\<span class="hljs-title">Type</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Database</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractGenerator</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">GeneratorInterface</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generate</span><span class="hljs-params">()</span>: <span class="hljs-title">void</span>
    </span>{
        <span class="hljs-comment">// create pages from a SQLite database</span>
        $db = <span class="hljs-keyword">new</span> SQLite3(<span class="hljs-string">'database.sqlite'</span>);
        $statement = $db-&gt;prepare(<span class="hljs-string">'SELECT * FROM blog'</span>);
        $result = $statement-&gt;execute();
        <span class="hljs-keyword">while</span> ($row = $result-&gt;fetchArray(SQLITE3_ASSOC)) {
            $page = (<span class="hljs-keyword">new</span> Page($row[<span class="hljs-string">'page-id'</span>]))
                -&gt;setType(Type::PAGE-&gt;value)
                -&gt;setPath($row[<span class="hljs-string">'path'</span>])
                -&gt;setBodyHtml($row[<span class="hljs-string">'html'</span>])
                -&gt;setVariable(<span class="hljs-string">'title'</span>, $row[<span class="hljs-string">'title'</span>])
                -&gt;setVariable(<span class="hljs-string">'date'</span>, $row[<span class="hljs-string">'date'</span>]);
            <span class="hljs-keyword">$this</span>-&gt;generatedPages-&gt;add($page);
        }
        $result-&gt;finalize();
        $db-&gt;close();
    }
}</code></pre>
<p><em>configuration</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">generators:</span>
    <span class="hljs-comment"># priority: class name</span>
    <span class="hljs-attr">99:</span> <span class="hljs-string">Cecil\Generator\DummyPage</span>
    <span class="hljs-attr">35:</span> <span class="hljs-string">Cecil\Generator\Database</span></code></pre>
<h2 id="twig-extension">Twig extension</h2>
<p>You can add custom <a href="/templates/reference/functions/">functions</a> and <a href="/templates/reference/filters/">filters</a>:</p>
<ol>
<li><a href="https://twig.symfony.com/doc/advanced.html#creating-an-extension" target="_blank" rel="noopener noreferrer">create a Twig extension</a> in the <code translate="no">Cecil\Renderer\Extension</code> namespace</li>
<li>add the PHP file in the <code translate="no">extensions</code> directory</li>
<li>add the class name to the configuration</li>
</ol>
<p><strong>Example:</strong></p>
<p><em>/extensions/Cecil/Renderer/Extension/MyTwigExtension.php</em></p>
<pre><code class="language-php hljs php" translate="no"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Renderer</span>\<span class="hljs-title">Extension</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyTwigExtension</span> <span class="hljs-keyword">extends</span> \<span class="hljs-title">Twig</span>\<span class="hljs-title">Extension</span>\<span class="hljs-title">AbstractExtension</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFilters</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-comment">// add a new filter named 'md5'</span>
        <span class="hljs-keyword">return</span> [
            <span class="hljs-keyword">new</span> \Twig\TwigFilter(<span class="hljs-string">'md5'</span>, <span class="hljs-string">'md5'</span>),
        ];
    }
}</code></pre>
<p><em>configuration</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">layouts:</span>
  <span class="hljs-attr">extensions:</span>
    <span class="hljs-attr">MyExtension:</span> <span class="hljs-string">Cecil\Renderer\Extension\MyTwigExtension</span></code></pre>
<h2 id="output-post-processor">Output Post Processor</h2>
<p>You can post process page output.</p>
<p>Just create a new PHP class in the <code translate="no">Cecil\Renderer\PostProcessor</code> namespace and add the class name to the `output.postprocessors list.</p>
<p><strong>Example:</strong></p>
<p><em>/extensions/Cecil/Renderer/PostProcessor/MyProcessor.php</em></p>
<pre><code class="language-php hljs php" translate="no"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Renderer</span>\<span class="hljs-title">PostProcessor</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">Cecil</span>\<span class="hljs-title">Collection</span>\<span class="hljs-title">Page</span>\<span class="hljs-title">Page</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyProcessor</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractPostProcessor</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">process</span><span class="hljs-params">(Page $page, string $output, string $format)</span>: <span class="hljs-title">string</span>
    </span>{
        <span class="hljs-comment">// add a meta tag to the head of the HTML output</span>
        <span class="hljs-keyword">if</span> ($format == <span class="hljs-string">'html'</span>) {
            <span class="hljs-keyword">if</span> (!preg_match(<span class="hljs-string">'/&lt;meta name="test".*/i'</span>, $output)) {
                $meta = \sprintf(<span class="hljs-string">'&lt;meta name="test" content="Test"&gt;'</span>);
                $output = preg_replace_callback(<span class="hljs-string">'/([[:blank:]]*)(&lt;\/head&gt;)/i'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-params">($matches)</span> <span class="hljs-title">use</span> <span class="hljs-params">($meta)</span> </span>{
                    <span class="hljs-keyword">return</span> str_repeat($matches[<span class="hljs-number">1</span>] ?: <span class="hljs-string">' '</span>, <span class="hljs-number">2</span>) . $meta . <span class="hljs-string">"\n"</span> . $matches[<span class="hljs-number">1</span>] . $matches[<span class="hljs-number">2</span>];
                }, $output);
            }
        }

        <span class="hljs-keyword">return</span> $output;
    }
}</code></pre>
<p><em>configuration</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">output:</span>
  <span class="hljs-attr">postprocessors:</span>
    <span class="hljs-attr">MyProcessor:</span> <span class="hljs-string">Cecil\Renderer\PostProcessor\MyProcessor</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/assets/images/</id>
    <title>Images</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/assets/images/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Images</h1>
<h2 id="image-srcset">image_srcset</h2>
<p>Builds the HTML img <code translate="no">srcset</code> (responsive) attribute of an image Asset.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ image_srcset(asset) }}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> asset = asset(image_path) %}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(asset) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset.width }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset.height }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"asset"</span> <span class="hljs-attr">srcset</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ image_srcset(asset) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ image_sizes('asset') }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span></code></pre>
<h2 id="image-sizes">image_sizes</h2>
<p>Returns the HTML img <code translate="no">sizes</code> attribute based on a CSS class name.<br>
It should be use in conjunction with the <a href="/documentation/images/#image-srcset"><code translate="no">image_srcset</code></a> function.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ image_sizes('class') }}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> asset = asset(image_path) %}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(asset) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset.width }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset.height }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"asset"</span> <span class="hljs-attr">srcset</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ image_srcset(asset) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">sizes</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ image_sizes('asset') }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span></code></pre>
<h2 id="image-from-website">image_from_website</h2>
<p>Builds the HTML img element from a website URL by extracting its illustration image.
Returns <code translate="no">null</code> if no image is found.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ image_from_website('url', {attributes}, {options}) }}</span></code></pre>
<p>The image is searched in the page HTML with the following fallbacks, the first candidate that can be downloaded as an image is used:</p>
<ol>
<li>Open Graph: <code translate="no">og:image:secure_url</code>, <code translate="no">og:image</code>, <code translate="no">og:image:url</code></li>
<li>Twitter: <code translate="no">twitter:image</code>, <code translate="no">twitter:image:src</code></li>
<li><code translate="no">&lt;link rel="image_src"&gt;</code></li>
<li>Microdata: <code translate="no">itemprop="image"</code></li>
<li>JSON-LD: <code translate="no">image</code> property</li>
<li>First <code translate="no">&lt;img&gt;</code> of <code translate="no">&lt;article&gt;</code>, <code translate="no">&lt;main&gt;</code> or <code translate="no">&lt;body&gt;</code></li>
<li><code translate="no">&lt;link rel="apple-touch-icon"&gt;</code></li>
<li><code translate="no">&lt;link rel="icon"&gt;</code></li>
</ol>
<p>Relative URLs are resolved against <code translate="no">&lt;base href&gt;</code> or the page URL.</p>
<p>The resolved image URL and the downloaded image are cached (see <a href="/configuration/cache/"><code translate="no">cache.assets.remote.ttl</code></a>).</p>
<p>Options:</p>
<ul>
<li><code translate="no">fallback</code>: image path (or URL) used if no image is found</li>
<li>other <a href="/templates/reference/functions/#html"><code translate="no">image</code></a> options (e.g.: <code translate="no">responsive</code>, <code translate="no">formats</code>)</li>
</ul>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ image_from_website('https://example.com/page-with-image.html') }}</span><span class="xml">

</span><span class="hljs-comment">{# with a fallback image #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ image_from_website('https://example.com/', {alt: 'Illustration'}, {fallback: 'images/default.png'}) }}</span></code></pre>
<h2 id="resize">resize</h2>
<p>Resizes an image to a specified width (in pixels) or/and height (in pixels).</p>
<ul>
<li>If only the width is specified, the height is calculated to preserve the aspect ratio</li>
<li>If only the height is specified, the width is calculated to preserve the aspect ratio</li>
<li>If both width and height are specified, the image is resized to fit within the given dimensions, image is cropped and centered if necessary</li>
<li>If remove_animation is true, any animation in the image (e.g., GIF) will be removed</li>
</ul>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(image_path)|resize(width: width, height: height, remove_animation: bool) }}</span></code></pre>
<aside class="note note-info"><p>The original file is not altered and the resized version is saved at <code translate="no">/thumbnails/&lt;width&gt;x&lt;height&gt;/image.jpg</code>.</p></aside>
<aside class="note note-tip"><p>ICO files are supported: the largest icon is resized and saved as a single icon ICO file (PNG compressed). Icons stored as BMP with a color depth other than 24 or 32 bits require the <a href="https://www.php.net/manual/book.imagick.php" target="_blank" rel="noopener noreferrer">Imagick</a> PHP extension: otherwise, the original ICO file is kept and a warning is logged.</p></aside>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(page.image)|resize(300) }}</span><span class="xml">
</span><span class="hljs-comment">{# equivalent to: #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ asset(page.image)|resize(width: 300) }}</span><span class="xml">
</span><span class="hljs-comment">{# resizes to 300px width, height auto-calculated to preserve aspect ratio #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ asset(page.image)|resize(height: 200) }}</span><span class="xml">
</span><span class="hljs-comment">{# resizes to 300px width and 200px height, and crops if necessary #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ asset(page.image)|resize(300, 200) }}</span><span class="xml">
</span><span class="hljs-comment">{# removes any animation from the image #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ asset(page.image)|resize(width: 1200, height: 630, remove_animation: true) }}</span></code></pre>
<h2 id="cover">cover</h2>
<p>Resizes an image to a specified width and height, cropping it if necessary.</p>
<aside class="note note-warning"><p>The <code translate="no">cover</code> filter is deprecated since version <ins>8.77</ins> and will be removed in future versions. Use the <a href="#resize"><code translate="no">resize</code></a> filter instead, with both width and height parameters.</p></aside>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(image_path)|cover(width, height) }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(page.image)|cover(1200, 630) }}</span></code></pre>
<h2 id="maskable">maskable</h2>
<p>Adds padding, in pourcentages, to an image to make it maskable.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(image_path)|maskable(padding) }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset('icon.png')|maskable }}</span></code></pre>
<h2 id="webp">webp</h2>
<p>Converts an image to <a href="https://developers.google.com/speed/webp" target="_blank" rel="noopener noreferrer">WebP</a> format.</p>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">picture</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">source</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/webp"</span> <span class="hljs-attr">srcset</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset(image_path)|webp }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(asset(image_path)) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset(image_path).width }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset(image_path).height }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">picture</span>&gt;</span></span></code></pre>
<h2 id="avif">avif</h2>
<p>Converts an image to <a href="https://github.com/AOMediaCodec/libavif" target="_blank" rel="noopener noreferrer">AVIF</a> format.</p>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">picture</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">source</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/avif"</span> <span class="hljs-attr">srcset</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset(image_path)|avif }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(asset(image_path)) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset(image_path).width }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ asset(image_path).height }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">picture</span>&gt;</span></span></code></pre>
<h2 id="lqip">lqip</h2>
<p>Returns a <a href="https://www.guypo.com/introducing-lqip-low-quality-image-placeholders" target="_blank" rel="noopener noreferrer">Low Quality Image Placeholder</a> (100x100 px, 50% blurred) as data URL.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(image_path)|lqip }}</span></code></pre>
<h2 id="dominant-color">dominant_color</h2>
<p>Returns the dominant <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color" target="_blank" rel="noopener noreferrer">hexadecimal color</a> of an image.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(image_path)|dominant_color }}</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/site/</id>
    <title>Site options</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/site/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Site options</h1>
<h2 id="title">title</h2>
<p>Main title of the site.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">title:</span> <span class="hljs-string">"&lt;site title&gt;"</span></code></pre>
<h2 id="baseline">baseline</h2>
<p>Short description (~ 20 characters).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">baseline:</span> <span class="hljs-string">"&lt;baseline&gt;"</span></code></pre>
<h2 id="baseurl">baseurl</h2>
<p>The base URL.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">baseurl:</span> <span class="hljs-string">&lt;url&gt;</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">baseurl:</span> <span class="hljs-string">http://localhost:8000/</span></code></pre>
<aside class="note note-important"><p><code translate="no">baseurl</code> should end with a trailing slash (<code translate="no">/</code>).</p></aside>
<h2 id="canonicalurl">canonicalurl</h2>
<p>If set to <code translate="no">true</code> the <a href="/templates/reference/functions/#url"><code translate="no">url()</code></a> function will return the absolute URL (<code translate="no">false</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">canonicalurl:</span> <span class="hljs-string">&lt;true|false&gt;</span> <span class="hljs-comment"># false by default</span></code></pre>
<h2 id="description">description</h2>
<p>Site description (~ 250 characters).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">description:</span> <span class="hljs-string">"&lt;description&gt;"</span></code></pre>
<h2 id="menus">menus</h2>
<p>Menus are used to create <a href="/templates/variables/#site-menus">navigation links in templates</a>.</p>
<p>A menu is made up of a unique ID and entry properties (name, URL, weight).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">menus:</span>
  <span class="hljs-string">&lt;name&gt;:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">&lt;unique-id&gt;</span>   <span class="hljs-comment"># unique identifier (required)</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">"&lt;name&gt;"</span>    <span class="hljs-comment"># name displayed in templates</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">&lt;url&gt;</span>        <span class="hljs-comment"># relative or absolute URL</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-string">&lt;integer&gt;</span> <span class="hljs-comment"># integer value used to sort entries (lighter first)</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">menus:</span>
  <span class="hljs-attr">main:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">about</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">"About"</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">/about/</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">footer:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">author</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">The</span> <span class="hljs-string">author</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">https://arnaudligny.fr</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">99</span></code></pre>
<aside class="note note-info"><p>A <code translate="no">main</code> menu is automatically created with the home page entry and all sections entries (<a href="/content/">See content management</a>)</p></aside>
<aside class="note note-tip"><p>A page can be added to a menu by setting the <a href="/content/front-matter/#menu"><code translate="no">menu</code> variable</a> in its front matter.</p></aside>
<h3 id="override-an-entry">Override an entry</h3>
<p>A page menu entry can be overridden: use the page ID as <code translate="no">id</code>.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">menus:</span>
  <span class="hljs-attr">main:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">index</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">"My amazing homepage!"</span>
      <span class="hljs-attr">weight:</span> <span class="hljs-number">1</span></code></pre>
<h3 id="disable-an-entry">Disable an entry</h3>
<p>A menu entry can be disabled with <code translate="no">enabled: false</code>.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">menus:</span>
  <span class="hljs-attr">main:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">about</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">false</span></code></pre>
<h2 id="taxonomies">taxonomies</h2>
<p>List of vocabularies, paired by plural and singular value.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">taxonomies:</span>
  <span class="hljs-string">&lt;plural&gt;:</span> <span class="hljs-string">&lt;singular&gt;</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">taxonomies:</span>
  <span class="hljs-attr">categories:</span> <span class="hljs-string">category</span>
  <span class="hljs-attr">tags:</span> <span class="hljs-string">tag</span></code></pre>
<p>Then you can use those vocabularies in your content’s <a href="/content/front-matter/#taxonomy">front matter</a>.</p>
<aside class="note note-warning"><p>Since <ins>version 8.37.0</ins>, default vocabularies <code translate="no">category</code> and <code translate="no">tag</code> have been removed. You must define them in the configuration file if you want to use them.</p></aside>
<aside class="note note-tip"><p>A vocabulary can be disabled with the special value <code translate="no">disabled</code>. Example: <code translate="no">tags: disabled</code>.</p></aside>
<h2 id="theme">theme</h2>
<p>The theme to use, or a list of themes.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">theme:</span> <span class="hljs-string">&lt;theme&gt;</span> <span class="hljs-comment"># theme name</span>
<span class="hljs-comment"># or</span>
<span class="hljs-attr">theme:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">&lt;theme1&gt;</span> <span class="hljs-comment"># theme name</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">&lt;theme2&gt;</span></code></pre>
<aside class="note note-info"><p>The first theme overrides the others, and so on.</p></aside>
<p><em>Examples:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">theme:</span> <span class="hljs-string">hyde</span></code></pre>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">theme:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">serviceworker</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">hyde</span></code></pre>
<aside class="note note-info"><p>See <a href="https://github.com/Cecilapp?q=theme#org-repositories" target="_blank" rel="noopener noreferrer">themes on GitHub</a> or on website <a href="https://cecil.app/themes/">themes section</a>.</p></aside>
<h2 id="date">date</h2>
<p>Date format and timezone.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">date:</span>
  <span class="hljs-attr">format:</span> <span class="hljs-string">&lt;format&gt;</span>     <span class="hljs-comment"># date format (optional, `F j, Y` by default)</span>
  <span class="hljs-attr">timezone:</span> <span class="hljs-string">&lt;timezone&gt;</span> <span class="hljs-comment"># date timezone (optional, local time zone by default)</span></code></pre>
<ul>
<li><code translate="no">format</code>: <a href="https://php.net/date" target="_blank" rel="noopener noreferrer">PHP date</a> format specifier</li>
<li><code translate="no">timezone</code>: see <a href="https://php.net/timezones" target="_blank" rel="noopener noreferrer">timezones</a></li>
</ul>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">date:</span>
  <span class="hljs-attr">format:</span> <span class="hljs-string">'j F, Y'</span>
  <span class="hljs-attr">timezone:</span> <span class="hljs-string">'Europe/Paris'</span></code></pre>
<h2 id="metatags">metatags</h2>
<p><em>metatags</em> are SEO and social helpers that can be automatically injected in the <code translate="no">&lt;head&gt;</code>, with the template <a href="https://github.com/Cecilapp/Cecil/blob/main/resources/layouts/partials/metatags.html.twig" target="_blank" rel="noopener noreferrer"><code translate="no">partials/metatags.html.twig</code></a>.</p>
<p>This template adds the following meta tags:</p>
<ul>
<li>Page title + Site title, or Site title + Site baseline</li>
<li>Page/Site description</li>
<li>Page/Site keywords</li>
<li>Page/Site author</li>
<li>Search engine crawler directives (<em>robots</em>)</li>
<li>Favicon links</li>
<li>Navigation links (first, previous, next, last)</li>
<li>Canonical URL</li>
<li>Alternate links (i.e.: RSS feed, others languages)</li>
<li><a href="https://developer.mozilla.org/docs/Web/HTML/Reference/Attributes/rel/me" target="_blank" rel="noopener noreferrer"><code translate="no">rel=me</code></a> links</li>
<li><a href="https://ogp.me" target="_blank" rel="noopener noreferrer">Open Graph</a></li>
<li>Facebook profile ID</li>
<li><a href="https://developer.x.com/docs/x-for-websites/cards/guides/getting-started" target="_blank" rel="noopener noreferrer">Twitter/X Card</a></li>
<li><a href="https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/" target="_blank" rel="noopener noreferrer">Fediverse tag</a></li>
<li><a href="https://www.dublincore.org/specifications/dublin-core/dcmi-terms/" target="_blank" rel="noopener noreferrer">Dublin Core</a></li>
<li><a href="https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data" target="_blank" rel="noopener noreferrer">Structured data</a> (JSON-LD)</li>
</ul>
<h3 id="metatags-options">metatags options</h3>
<p>Cecil uses page front matter to feed meta tags, and falls back to site options when needed.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">title:</span> <span class="hljs-string">"Page/Site title"</span>              <span class="hljs-comment"># used by title meta</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">"Page/Site description"</span>  <span class="hljs-comment"># used by description meta</span>
<span class="hljs-attr">tags:</span> <span class="hljs-string">[tag1,</span> <span class="hljs-string">tag2]</span>                    <span class="hljs-comment"># used by keywords meta</span>
<span class="hljs-attr">keywords:</span> <span class="hljs-string">[keyword1,</span> <span class="hljs-string">keyword2]</span>        <span class="hljs-comment"># obsolete</span>
<span class="hljs-attr">author:</span>                               <span class="hljs-comment"># used by author meta</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">&lt;name&gt;</span>                          <span class="hljs-comment"># author name</span>
  <span class="hljs-attr">url:</span> <span class="hljs-string">&lt;url&gt;</span>                            <span class="hljs-comment"># author URL</span>
  <span class="hljs-attr">email:</span> <span class="hljs-string">&lt;email&gt;</span>                        <span class="hljs-comment"># author email</span>
<span class="hljs-attr">image:</span> <span class="hljs-string">image.jpg</span>                      <span class="hljs-comment"># used by Open Graph and social networks cards</span>
<span class="hljs-attr">canonical:</span>                            <span class="hljs-comment"># used to override the generated canonical URL</span>
  <span class="hljs-attr">url:</span> <span class="hljs-string">&lt;URL&gt;</span>                            <span class="hljs-comment"># absolute URL</span>
  <span class="hljs-attr">title:</span> <span class="hljs-string">"&lt;URL title&gt;"</span>                  <span class="hljs-comment"># optional canonical title</span>
<span class="hljs-attr">social:</span>                               <span class="hljs-comment"># used by social networks meta</span>
  <span class="hljs-attr">twitter:</span>                              <span class="hljs-comment"># used by Twitter/X Card</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">&lt;URL&gt;</span>                            <span class="hljs-comment"># used for `rel=me` link</span>
    <span class="hljs-attr">site:</span> <span class="hljs-string">username</span>                        <span class="hljs-comment"># site username</span>
    <span class="hljs-attr">creator:</span> <span class="hljs-string">username</span>                     <span class="hljs-comment"># page author username</span>
  <span class="hljs-attr">mastodon:</span>                             <span class="hljs-comment"># used by Mastodon meta</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">&lt;URL&gt;</span>                            <span class="hljs-comment"># used for `rel=me` link</span>
    <span class="hljs-attr">creator:</span> <span class="hljs-string">handle</span>                       <span class="hljs-comment"># page author account</span>
  <span class="hljs-attr">facebook:</span>                             <span class="hljs-comment"># used by Facebook meta</span>
    <span class="hljs-attr">url:</span> <span class="hljs-string">&lt;URL&gt;</span>                            <span class="hljs-comment"># used for `rel=me` link</span>
    <span class="hljs-attr">id:</span> <span class="hljs-number">123456789</span>                         <span class="hljs-comment"># Facebook profile ID</span>
    <span class="hljs-attr">username:</span> <span class="hljs-string">username</span>                    <span class="hljs-comment"># page author username</span></code></pre>
<aside class="note note-tip"><p>If needed, <code translate="no">title</code> and <code translate="no">image</code> can be overridden:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ <span class="hljs-name">include</span><span class="hljs-params">('partials/metatags.html.twig', {title: 'Custom title', image: og_image})</span> }}</span></code></pre></aside>
<h3 id="metatags-configuration">metatags configuration</h3>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">metatags:</span>
  <span class="hljs-attr">title:</span>                   <span class="hljs-comment"># title options</span>
    <span class="hljs-attr">divider:</span> <span class="hljs-string">" &amp;middot; "</span>    <span class="hljs-comment"># string between page title and site title</span>
    <span class="hljs-attr">only:</span> <span class="hljs-literal">false</span>              <span class="hljs-comment"># displays page title only (`false` by default)</span>
    <span class="hljs-attr">pagination:</span>              <span class="hljs-comment"># pagination options</span>
      <span class="hljs-attr">shownumber:</span> <span class="hljs-literal">true</span>         <span class="hljs-comment"># displays page number in title (`true` by default)</span>
      <span class="hljs-attr">label:</span> <span class="hljs-string">"Page %s"</span>         <span class="hljs-comment"># how to display page number (`Page %s` by default)</span>
  <span class="hljs-attr">robots:</span> <span class="hljs-string">"index,follow"</span>   <span class="hljs-comment"># web crawlers directives (`index,follow` by default)</span>
  <span class="hljs-attr">favicon:</span>                 <span class="hljs-comment"># favicon options</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>            <span class="hljs-comment"># includes favicon (`true` by default)</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">favicon.png</span>       <span class="hljs-comment"># path to favicon image</span>
    <span class="hljs-attr">sizes:</span>                   <span class="hljs-comment"># sizes by device</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">"icon":</span> <span class="hljs-string">[32,</span> <span class="hljs-number">57</span><span class="hljs-string">,</span> <span class="hljs-number">76</span><span class="hljs-string">,</span> <span class="hljs-number">96</span><span class="hljs-string">,</span> <span class="hljs-number">128</span><span class="hljs-string">,</span> <span class="hljs-number">192</span><span class="hljs-string">,</span> <span class="hljs-number">228</span><span class="hljs-string">]</span>  <span class="hljs-comment"># web browsers</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">"shortcut icon":</span> <span class="hljs-string">[196]</span>                   <span class="hljs-comment"># Android</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">"apple-touch-icon":</span> <span class="hljs-string">[120,</span> <span class="hljs-number">152</span><span class="hljs-string">,</span> <span class="hljs-number">180</span><span class="hljs-string">]</span>      <span class="hljs-comment"># iOS</span>
  <span class="hljs-attr">navigation:</span> <span class="hljs-literal">true</span>         <span class="hljs-comment"># includes previous and next links (`true` by default)</span>
  <span class="hljs-attr">image:</span> <span class="hljs-literal">true</span>              <span class="hljs-comment"># includes image (`true` by default)</span>
  <span class="hljs-attr">og:</span> <span class="hljs-literal">true</span>                 <span class="hljs-comment"># includes Open Graph meta tags (`true` by default)</span>
  <span class="hljs-attr">articles:</span> <span class="hljs-string">"blog"</span>         <span class="hljs-comment"># articles' section (`blog` by default)</span>
  <span class="hljs-attr">twitter:</span> <span class="hljs-literal">true</span>            <span class="hljs-comment"># includes Twitter/X Card meta tags (`true` by default)</span>
  <span class="hljs-attr">mastodon:</span> <span class="hljs-literal">true</span>           <span class="hljs-comment"># includes Mastodon meta tags (`true` by default)</span>
  <span class="hljs-attr">dc:</span> <span class="hljs-literal">false</span>                <span class="hljs-comment"># includes Dublin Core meta tags (`false` by default)</span>
  <span class="hljs-attr">data:</span> <span class="hljs-literal">false</span>              <span class="hljs-comment"># includes JSON-LD structured data (`false` by default)</span></code></pre>
<h2 id="debug">debug</h2>
<p>Enables the <em>debug mode</em>, used to display debug information like very verbose logs, Twig dump, Twig profiler, SCSS sourcemap, etc.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">debug:</span> <span class="hljs-literal">true</span></code></pre>
<p>There are two other ways to enable <em>debug mode</em>:</p>
<ol>
<li>Run a command with the <code translate="no">-vvv</code> option</li>
<li>Set the <code translate="no">CECIL_DEBUG</code> environment variable to <code translate="no">true</code></li>
</ol>
<hr>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/content/pages/</id>
    <title>Pages and sections</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-06T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/content/pages/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Pages and sections</h1>
<p>A page is a file made up of a <a href="#front-matter"><strong>front matter</strong></a> and a <a href="#body"><strong>body</strong></a>.</p>
<h2 id="front-matter">Front matter</h2>
<p>The <em>front matter</em> is a collection of <a href="/documentation/front-matter/">variables</a> (in <em>key/value</em> format) surrounded by <code translate="no">---</code>.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">"The title"</span>
<span class="hljs-attr">date:</span> <span class="hljs-number">2019</span><span class="hljs-number">-02</span><span class="hljs-number">-21</span>
<span class="hljs-attr">tags:</span> <span class="hljs-string">[tag</span> <span class="hljs-number">1</span><span class="hljs-string">,</span> <span class="hljs-string">tag</span> <span class="hljs-number">2</span><span class="hljs-string">]</span>
<span class="hljs-attr">customvar:</span> <span class="hljs-string">"Value of customvar"</span>
<span class="hljs-meta">---</span></code></pre>
<aside class="note note-info"><p>You can also use <code translate="no">&lt;!-- --&gt;</code> or <code translate="no">+++</code> as separator.</p></aside>
<h2 id="body">Body</h2>
<p><em>Body</em> is the main content of a page, it could be written in <a href="/documentation/markdown/">Markdown</a> or in plain text.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no"><span class="hljs-section"># Header</span>

[toc]

<span class="hljs-section">## Sub-Header 1</span>

Lorem ipsum dolor [<span class="hljs-string">sit amet</span>](<span class="hljs-link">https://example.com</span>), consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
<span class="xml"><span class="hljs-comment">&lt;!-- excerpt --&gt;</span></span>
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

<span class="hljs-section">## Sub-Header 2</span>

![<span class="hljs-string">Description</span>](<span class="hljs-link">/image.jpg "Title"</span>)

<span class="hljs-section">## Sub-Header 3</span>

:::tip
This is advice.
:::</code></pre>
<h2 id="file-prefix">File prefix</h2>
<p>The filename can contain a prefix to define <code translate="no">date</code> or <code translate="no">weight</code> variables of the page (used by <a href="/templates/reference/sorts/#sort-by-date"><code translate="no">sortby</code></a>).</p>
<aside class="note note-info"><p>Default prefix separators: <code translate="no">_</code> and <code translate="no">-</code>.</p>
<p>You can customize them with the <a href="/configuration/pages/#pages-prefix-separator"><code translate="no">pages.prefix.separator</code></a> option.</p></aside>
<h3 id="date">date</h3>
<p>The <em>date prefix</em> is used to set the <code translate="no">date</code> of the page, and must be a valid date format (i.e.: « YYYY-MM-DD »).</p>
<p><em>Example:</em></p>
<p>In « 2019-04-23_My blog post.md »:</p>
<ul>
<li>the prefix is « 2019-04-23 »</li>
<li>the <code translate="no">date</code> of the page is « 2019-04-23 »</li>
<li>the <code translate="no">title</code> of the page is « My blog post »</li>
</ul>
<h3 id="weight">weight</h3>
<p>The <em>weight prefix</em> is used to set the sort order of the page, and must be a valid integer value.</p>
<p><em>Example:</em></p>
<p>In « 1_The first project.md »:</p>
<ul>
<li>the prefix is « 1 »</li>
<li>the <code translate="no">weight</code> of the page is « 1 »</li>
<li>the <code translate="no">title</code> of the page is « The first project »</li>
</ul>
<h2 id="section">Section</h2>
<p>Some dedicated variables can be used in a custom <em>Section</em> (i.e.: <code translate="no">&lt;section&gt;/index.md</code>).</p>
<h3 id="sortby">sortby</h3>
<p>The order of pages in a <em>Section</em> can be changed.</p>
<p>Available values are:</p>
<ul>
<li><code translate="no">date</code>: more recent first</li>
<li><code translate="no">title</code>: alphabetic order</li>
<li><code translate="no">weight</code>: lightest first</li>
</ul>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">sortby:</span> <span class="hljs-string">title</span>
<span class="hljs-meta">---</span></code></pre>
<p><strong>More options:</strong></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">sortby:</span>
  <span class="hljs-attr">variable:</span> <span class="hljs-string">date</span>    <span class="hljs-comment"># "date", "updated", "title" or "weight"</span>
  <span class="hljs-attr">desc_title:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># used with "date" or "updated" variable value to sort by desc title order if items have the same date</span>
  <span class="hljs-attr">reverse:</span> <span class="hljs-literal">false</span>    <span class="hljs-comment"># reversed if true</span>
<span class="hljs-meta">---</span></code></pre>
<h3 id="pagination">pagination</h3>
<p>The global <a href="/configuration/pages/#pages-pagination">pagination configuration</a> is used by default, but you can change it for a specific <em>Section</em>.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">pagination:</span>
  <span class="hljs-attr">max:</span> <span class="hljs-number">5</span>
  <span class="hljs-attr">path:</span> <span class="hljs-string">"page"</span>
<span class="hljs-meta">---</span></code></pre>
<p>Pagination can be disabled for a <em>Section</em>:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">pagination:</span> <span class="hljs-literal">false</span>
<span class="hljs-meta">---</span></code></pre>
<h3 id="cascade">cascade</h3>
<p>Any variables in <code translate="no">cascade</code> are added to the front matter of all <em>sub pages</em>.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">cascade:</span>
  <span class="hljs-attr">banner:</span> <span class="hljs-string">image.jpg</span>
<span class="hljs-meta">---</span></code></pre>
<aside class="note note-info"><p>Existing variables are not overridden.</p></aside>
<h3 id="circular">circular</h3>
<p>Set <code translate="no">circular</code> to <code translate="no">true</code> to enable circular navigation with <a href="/templates/variables/#page-prev-next"><em>page.&lt;prev/next&gt;</em></a>.</p>
<aside class="note note-info"><p>With <a href="#sub-section">sub-sections</a>, only the <code translate="no">circular</code> value of the top level <em>Section</em> is used.</p></aside>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">circular:</span> <span class="hljs-literal">true</span>
<span class="hljs-meta">---</span></code></pre>
<h3 id="sub-section">Sub-section</h3>
<p>A nested folder that explicitly contains an <code translate="no">index.md</code> file is turned into a <em>sub-section</em> of its parent <em>Section</em>.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">&lt;mywebsite&gt;
└─ pages
   └─ blog                 &lt;- Section
      ├─ index.md
      ├─ post-1.md         &lt;- Page in Section "blog"
      └─ 2024              &lt;- Sub-section (contains an "index.md")
         ├─ index.md
         └─ post-2.md      &lt;- Page in Section "blog" *and* sub-section "blog/2024"</code></pre>
<p>A <em>sub-section</em>:</p>
<ul>
<li>is a <em>Section</em> (same type, variables and <a href="/templates/lookup-rules/#type-section">layout</a> resolution) available at its own URL (e.g.: <code translate="no">/blog/2024/</code>)</li>
<li>is rendered with the layouts of its parent <em>Sections</em> if it doesn't have its own (e.g.: <code translate="no">blog/list.html.twig</code>)</li>
<li>can be nested at any depth (e.g.: <code translate="no">blog/2024/06/</code>)</li>
<li>lists its own pages, and its pages also belong to each of their parent <em>Sections</em></li>
<li>is <strong>not</strong> listed in its parent <em>Section</em></li>
<li>is placed in the <a href="/templates/variables/#page-prev-next"><em>page.&lt;prev/next&gt;</em></a> navigation of its parent <em>Section</em> (according to its <code translate="no">sortby</code>), followed by its own pages</li>
</ul>
<aside class="note note-info"><p>A nested folder <strong>without</strong> an <code translate="no">index.md</code> file is not a <em>sub-section</em>: its pages simply belong to the parent <em>Section</em>.</p></aside>
<h2 id="home-page">Home page</h2>
<p>Like another section, <em>Home page</em> support <code translate="no">sortby</code> and <code translate="no">pagination</code> configuration.</p>
<h3 id="pagesfrom">pagesfrom</h3>
<p>Set a valid <em>Section</em> name in <code translate="no">pagesfrom</code> to use pages collection from this <em>Section</em> in <em>Home page</em>.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">pagesfrom:</span> <span class="hljs-string">blog</span>
<span class="hljs-meta">---</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/templates/lookup-rules/</id>
    <title>Organization and lookup rules</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/templates/lookup-rules/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Organization and lookup rules</h1>
<h2 id="files-organization">Files organization</h2>
<h3 id="kinds-of-templates">Kinds of templates</h3>
<p>There are three kinds of templates: <strong><em>layouts</em></strong>, <strong><em>components</em></strong>, and <strong><em>other templates</em></strong>. <em>Layouts</em> are used to render <a href="/content/pages/">pages</a>, and each layout can <a href="https://twig.symfony.com/doc/templates.html#including-other-templates" target="_blank" rel="noopener noreferrer">include templates</a> and <a href="/documentation/components/">components</a>.</p>
<h3 id="naming-convention">Naming convention</h3>
<p>Template files are stored in the <code translate="no">layouts/</code> directory and must be named according to the following convention:</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">layouts/(&lt;section&gt;/)&lt;type&gt;|&lt;layout&gt;.&lt;format&gt;(.&lt;language&gt;).twig</code></pre>
<dl>
<dt><code translate="no">&lt;section&gt;</code> (<em>optional</em>)</dt>
<dd>The section of the page (e.g.: <code translate="no">blog</code>).</dd>
<dt><code translate="no">&lt;type&gt;</code></dt>
<dd>The page type: <code translate="no">home</code> (or <code translate="no">index</code>) for <em>homepage</em>, <code translate="no">list</code> for <em>list</em>, <code translate="no">page</code> for <em>page</em>, etc. (See <a href="#lookup-rules"><em>Lookup rules</em></a> for details).</dd>
<dt><code translate="no">&lt;layout&gt;</code> (<em>optional</em>)</dt>
<dd>The custom layout name defined in the <a href="/content/pages/#front-matter">front matter</a> of the page (e.g.: <code translate="no">layout: my-layout</code>).</dd>
<dt><code translate="no">&lt;format&gt;</code></dt>
<dd>The <a href="/configuration/output/#output-formats">output format</a> of the rendered page (e.g.: <code translate="no">html</code>, <code translate="no">rss</code>, <code translate="no">json</code>, <code translate="no">xml</code>, etc.).</dd>
<dt><code translate="no">&lt;language&gt;</code> (<em>optional</em>)</dt>
<dd>The language of the page (e.g.: <code translate="no">fr</code>).</dd>
</dl>
<p><em>Examples:</em></p>
<pre><code class="language-plaintext hljs plaintext" translate="no">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"</code></pre>
<pre><code class="language-plaintext hljs plaintext" translate="no">&lt;my-site&gt;
├─ ...
├─ layouts
|  ├─ index.html.twig      # Used by type "homepage"
|  ├─ list.html.twig       # Used by types "homepage" and "section"
|  ├─ list.rss.twig        # Used by types "homepage" and "section", for RSS output format
|  ├─ page.html.twig       # Used by type "page"
|  ├─ taxonomy
|  |  ├─ tags.html.twig    # Used by type "vocabulary" of `tags` (list of terms)
|  |  └─ tag.html.twig     # Used by type "term" of `tags` (list of pages)
|  ├─ my-layout.html.twig  # Used by pages with `layout: my-layout` in the front matter
|  ├─ ...
|  └─ partials             # Included templates
|     ├─ footer.html.twig
|     └─ ...
└─ themes                  # Themes layouts and templates
   └─ ...</code></pre>
<h3 id="built-in-templates">Built-in templates</h3>
<p>Cecil comes with a set of <a href="https://github.com/Cecilapp/Cecil/tree/main/resources/layouts" target="_blank" rel="noopener noreferrer">built-in templates</a>.</p>
<aside class="note note-tip"><p>If you need to modify built-in templates, you can easily extract them via the following command: they will be copied in the <code translate="no">layouts</code> directory of your site.</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar util:templates:extract</code></pre></aside>
<h2 id="lookup-rules">Lookup rules</h2>
<p>In most of cases <strong>you don’t need to specify the layout</strong>: Cecil selects the most appropriate layout, according to the <strong>page type</strong>.</p>
<p>For example, the HTML output of <strong>home page</strong> (<code translate="no">index.md</code>) will be rendered:</p>
<ol>
<li>with <code translate="no">my-layout.html.twig</code> if the <code translate="no">layout</code> variable is set to "my-layout" (in the front matter)</li>
<li>if not, with <code translate="no">index.html.twig</code> if the file exists</li>
<li>if not, with <code translate="no">home.html.twig</code> if the file exists</li>
<li>if not, with <code translate="no">list.html.twig</code> if the file exists</li>
</ol>
<p>All rules are detailed below, for each page type, in the priority order.</p>
<h3 id="type-homepage">Type <em>homepage</em></h3>
<ol>
<li><code translate="no">&lt;layout&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">index.&lt;format&gt;.twig</code></li>
<li><code translate="no">home.&lt;format&gt;.twig</code></li>
<li><code translate="no">list.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/&lt;layout&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/index.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/home.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/list.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/page.&lt;format&gt;.twig</code></li>
</ol>
<h3 id="type-page">Type <em>page</em></h3>
<ol>
<li><code translate="no">&lt;section&gt;/&lt;layout&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">&lt;layout&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">&lt;section&gt;/page.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/&lt;layout&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">page.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/page.&lt;format&gt;.twig</code></li>
</ol>
<h3 id="type-section">Type <em>section</em></h3>
<ol>
<li><code translate="no">&lt;layout&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">&lt;section&gt;/index.&lt;format&gt;.twig</code></li>
<li><code translate="no">&lt;section&gt;/list.&lt;format&gt;.twig</code></li>
<li><code translate="no">section/&lt;section&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">&lt;parent&gt;/index.&lt;format&gt;.twig</code>, <code translate="no">&lt;parent&gt;/list.&lt;format&gt;.twig</code> and <code translate="no">section/&lt;parent&gt;.&lt;format&gt;.twig</code>, for each parent section of a sub-section (nearest first)</li>
<li><code translate="no">_default/section.&lt;format&gt;.twig</code></li>
<li><code translate="no">list.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/list.&lt;format&gt;.twig</code></li>
</ol>
<aside class="note note-tip"><p>The <code translate="no">&lt;section&gt;</code> of a <a href="/content/pages/#sub-section">sub-section</a> is its full path (e.g.: <code translate="no">blog/2024</code>), and a sub-section falls back to the templates of its parent sections: if <code translate="no">blog/2024/list.html.twig</code> doesn’t exist, the sub-section <code translate="no">blog/2024</code> is rendered with <code translate="no">blog/list.html.twig</code>.</p></aside>
<h3 id="type-vocabulary">Type <em>vocabulary</em></h3>
<ol>
<li><code translate="no">taxonomy/&lt;plural&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">vocabulary.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/vocabulary.&lt;format&gt;.twig</code></li>
</ol>
<h3 id="type-term">Type <em>term</em></h3>
<ol>
<li><code translate="no">taxonomy/&lt;plural&gt;/&lt;term&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">taxonomy/&lt;singular&gt;.&lt;format&gt;.twig</code></li>
<li><code translate="no">term.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/term.&lt;format&gt;.twig</code></li>
<li><code translate="no">_default/list.&lt;format&gt;.twig</code></li>
</ol>
<aside class="note note-important"><p>The <strong>vocabulary</strong> template is named after the <strong>plural</strong> (e.g.: <code translate="no">taxonomy/categories.html.twig</code> for <code translate="no">/categories/</code>), whereas the <strong>term</strong> template is named after the <strong>singular</strong> (e.g.: <code translate="no">taxonomy/category.html.twig</code> for <code translate="no">/categories/data-sovereignty/</code>).</p></aside>
<aside class="note note-tip"><p><code translate="no">&lt;term&gt;</code> is the slugified term name: a dedicated template for the term "Data Sovereignty" of the <code translate="no">categories</code> vocabulary is <code translate="no">taxonomy/categories/data-sovereignty.html.twig</code>.</p></aside>
<aside class="note note-info"><p>Most of those layouts are available by default, see <a href="https://github.com/Cecilapp/Cecil/tree/main/resources/layouts" target="_blank" rel="noopener noreferrer">built-in templates</a>.</p></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/templates/reference/functions/</id>
    <title>Functions</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/templates/reference/functions/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Functions</h1>
<blockquote>
<p><a href="https://twig.symfony.com/doc/functions/index.html" target="_blank" rel="noopener noreferrer">Functions</a> can be called to generate content. Functions are called by their name followed by parentheses (<code translate="no">()</code>) and may have arguments.</p>
</blockquote>
<h2 id="url">url</h2>
<p>Creates a valid URL for a page, a menu entry, an asset, a page ID or a path.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ url(value, {options}) }}</span></code></pre>
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody>
<tr>
<td>canonical</td>
<td>Prefix URL with <a href="/configuration/site/#baseurl"><code translate="no">baseurl</code></a> or use <a href="/configuration/site/#metatags-options"><code translate="no">canonical.url</code></a> if exists.</td>
<td>boolean</td>
<td><code translate="no">false</code></td>
</tr>
<tr>
<td>format</td>
<td>Defines page <a href="/configuration/output/#output-formats">output format</a> (e.g.: <code translate="no">json</code>).</td>
<td>string</td>
<td><code translate="no">html</code></td>
</tr>
<tr>
<td>language</td>
<td>Defines page <a href="/configuration/languages/#language">language</a> (e.g.: <code translate="no">fr</code>).</td>
<td>string</td>
<td>null</td>
</tr>
</tbody>
</table>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-comment">{# page #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ url(page) }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ url(page, {canonical: true}) }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ url(page, {format: json}) }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ url(page, {language: fr}) }}</span><span class="xml">
</span><span class="hljs-comment">{# menu entry #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ url(site.menus.main.about) }}</span><span class="xml">
</span><span class="hljs-comment">{# asset #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ url(asset('styles.css')) }}</span><span class="xml">
</span><span class="hljs-comment">{# page ID #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ url('page-id') }}</span><span class="xml">
</span><span class="hljs-comment">{# path #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ url('about-me/') }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ url('tags/' ~ tag) }}</span></code></pre>
<aside class="note note-info"><p>For convenience the <code translate="no">url</code> function is also available as a filter:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-comment">{# page #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ page|url }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ page|url({canonical: true, format: json, language: fr}) }}</span><span class="xml">
</span><span class="hljs-comment">{# asset #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ asset('styles.css')|url }}</span></code></pre></aside>
<aside class="note note-tip"><p>When the value is a string, <code translate="no">url()</code> slugifies it to find a matching page ID (e.g.: <code translate="no">url('tags/My Tag')</code> returns the URL of the page <code translate="no">tags/my-tag</code>). If no page matches, the string is kept as a path, with invalid characters (e.g.: spaces) percent-encoded.</p></aside>
<h2 id="html">html</h2>
<p>Creates an HTML element from an asset (or an array of assets with custom attributes).</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ html(asset, {attributes}, {options}) }}</span><span class="xml">
</span><span class="hljs-comment">{# dedicated functions for each common type of asset #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ css(asset) }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ js(asset) }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ image(asset) }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ audio(asset) }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ video(asset) }}</span></code></pre>
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>attributes</td>
<td>Adds <code translate="no">name="value"</code> couple to the HTML element.</td>
<td>array</td>
</tr>
<tr>
<td>options</td>
<td><code translate="no">{preload: boolean}</code>: preloads.<br>For images:<br><code translate="no">{formats: array}</code>: adds alternative formats.<br><code translate="no">{responsive: bool|string}</code>: adds responsive images (based on <code translate="no">width</code> or pixels <code translate="no">density</code>).<br><code translate="no">{placeholder: string}</code>: fills the image background before loading (<code translate="no">color</code> or <code translate="no">lqip</code>).</td>
<td>array</td>
</tr>
</tbody>
</table>
<aside class="note note-warning"><p>Since version <ins>8.42.0</ins>, the <code translate="no">html</code> function replace the deprecated <code translate="no">html</code> filter.</p></aside>
<aside class="note note-tip"><p>You can define a global default behavior of images options (<code translate="no">formats</code>, <code translate="no">responsive</code> and <code translate="no">placeholder</code>) through the <a href="/configuration/layouts/#layouts-images">layouts configuration</a>.</p>
<p>When <a href="/configuration/layouts/#layouts-images"><code translate="no">layouts.images.dark_suffix</code></a> is configured (e.g. <code translate="no">.dark</code>), Cecil automatically looks for a dark variant of each image (e.g. <code translate="no">photo.dark.jpg</code> alongside <code translate="no">photo.jpg</code>) and generates a <code translate="no">&lt;picture&gt;</code> element with a <code translate="no">&lt;source media="(prefers-color-scheme: dark)"&gt;</code>.</p>
<p>In the same way, when <a href="/configuration/layouts/#layouts-images"><code translate="no">layouts.images.mobile_suffix</code></a> is configured (e.g. <code translate="no">.mobile</code>), Cecil looks for a mobile variant of each image (e.g. <code translate="no">photo.mobile.jpg</code>) and adds a <code translate="no">&lt;source&gt;</code> with the <a href="/configuration/layouts/#layouts-images"><code translate="no">layouts.images.mobile_media_query</code></a> media query. If a dark variant of the mobile image exists (e.g. <code translate="no">photo.mobile.dark.jpg</code>), it is used on mobile with dark color scheme.</p></aside>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-comment">{# CSS with an attribute #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html(asset('print.css'), {media: 'print'}) }}</span><span class="xml">
</span><span class="hljs-comment">{# CSS with an attribute and an option #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html(asset('styles.css'), {title: 'Main theme'}, {preload: true}) }}</span><span class="xml">
</span><span class="hljs-comment">{# Array of assets with media query #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html([
  {asset: asset('css/style.css')},
  {asset: asset('css/style-dark.css'), attributes: {media: '(prefers-color-scheme: dark)'}}</span><span class="xml">
]) }}
</span><span class="hljs-comment">{# JavaScript #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html(asset('script.js')) }}</span><span class="xml">
</span><span class="hljs-comment">{# image without specific attributes nor options #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html(asset('image.png')) }}</span><span class="xml">
</span><span class="hljs-comment">{# image with specific attributes, responsive images and alternative formats #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {responsive: true, formats: ['avif', 'webp']}) }}</span><span class="xml">
</span><span class="hljs-comment">{# image with responsive pixels density images #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html(asset('image.jpg'), options={responsive: 'density'}, attributes={width: 256}) }}</span><span class="xml">
</span><span class="hljs-comment">{# image with a Low-Quality Image Placeholder #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {placeholder: 'lqip'}) }}</span><span class="xml">
</span><span class="hljs-comment">{# Audio #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html(asset('audio.mp3')) }}</span><span class="xml">
</span><span class="hljs-comment">{# Video #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ html(asset('video.mp4')) }}</span></code></pre>
<aside class="note note-info"><p>For convenience the <code translate="no">html</code> function stay available as a filter (but is considered as deprecated):</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset|html({attributes}, {options}) }}</span></code></pre></aside>
<h2 id="readtime">readtime</h2>
<p>Determines read time of a text, in minutes.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ readtime(value) }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ readtime(page.content) }}</span><span class="xml"> min</span></code></pre>
<h2 id="hash">hash</h2>
<p>Calculates the hash of an object, an array or a string with a given algorithm.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ hash(value, algorithm) }}</span></code></pre>
<p><code translate="no">algorithm</code> can be any algorithm supported by PHP's <code translate="no">hash()</code> function (e.g.: <code translate="no">md5</code>, <code translate="no">sha256</code>, etc.). Default is <code translate="no">xxh128</code>.</p>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ hash('my string', 'sha256') }}</span></code></pre>
<h2 id="cache-key">cache_key</h2>
<p>Calculates a cache key for <a href="/cache/#fragments-cache"><em>fragments</em> cache</a> based on a name and an optional value.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name">cache</span> cache_key(name, value) %}</span><span class="xml">
  </span><span class="hljs-comment">{# cacheable content #}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name">endcache</span> %}</span></code></pre>
<p>The function adds a hash of the value (could be a string, an array or an object) to the name (and the current language and build ID to be sure the generated cache key is unique) so if the value is changed the cache key is changed too and the cache is automatically cleared.</p>
<h2 id="getenv">getenv</h2>
<p>Gets the value of an environment variable from its key.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ getenv(var) }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ getenv('VAR') }}</span></code></pre>
<h2 id="dump">dump</h2>
<p>The <code translate="no">dump</code> 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:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ <span class="hljs-name">dump</span><span class="hljs-params">(user)</span> }}</span></code></pre>
<aside class="note note-important"><p>The <a href="/configuration/site/#debug"><em>debug mode</em></a> must be enabled.</p></aside>
<h2 id="d">d</h2>
<p>The <code translate="no">d()</code> function is the HTML version of <a href="#dump"><code translate="no">dump()</code></a> and use the <a href="https://symfony.com/doc/5.4/components/var_dumper.html" target="_blank" rel="noopener noreferrer">Symfony VarDumper Component</a> behind the scenes.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ d(variable, {theme: light}) }}</span></code></pre>
<ul>
<li>If <em>variable</em> is not provided then the function returns the current Twig context</li>
<li>Available themes are « light » (default) and « dark »</li>
</ul>
<aside class="note note-important"><p>The <a href="/configuration/site/#debug"><em>debug mode</em></a> must be enabled.</p></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/assets/processing/</id>
    <title>CSS, JavaScript and processing</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/assets/processing/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>CSS, JavaScript and processing</h1>
<h2 id="fingerprint">fingerprint</h2>
<p>Add the file content finger print to the file name.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(path)|fingerprint }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ path|fingerprint }}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset('styles.css')|fingerprint }}</span></code></pre>
<h2 id="minify">minify</h2>
<p>Minifying a CSS or a JavaScript file.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(path)|minify }}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset('styles.css')|minify }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ asset('scripts.js')|minify }}</span></code></pre>
<h2 id="minify-css">minify_css</h2>
<p>Minifying a CSS string.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ variable|minify_css }}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">apply</span></span> minify_css %}</span><span class="xml">
</span><span class="hljs-comment">{# CSS here #}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endapply</span></span> %}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> styles = 'some CSS here' %}</span><span class="xml">
</span><span class="hljs-template-variable">{{ styles|minify_css }}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">apply</span></span> minify_css %}</span><span class="xml"><span class="css">
  <span class="hljs-selector-tag">html</span> {
    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fcfcfc</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#444</span>;
  }
</span></span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endapply</span></span> %}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span></code></pre>
<h2 id="minify-js">minify_js</h2>
<p>Minifying a JavaScript string.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ variable|minify_js }}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">apply</span></span> minify_js %}</span><span class="xml">
</span><span class="hljs-comment">{# JavaScript here #}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endapply</span></span> %}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> script = 'some JavaScript here' %}</span><span class="xml">
</span><span class="hljs-template-variable">{{ script|minify_js }}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">apply</span></span> minify_js %}</span><span class="xml"><span class="javascript">
  <span class="hljs-keyword">var</span> test = <span class="hljs-string">'test'</span>;
  <span class="hljs-built_in">console</span>.log(test);
</span></span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endapply</span></span> %}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span></code></pre>
<h2 id="scss-to-css">scss_to_css</h2>
<p>Compiles a <a href="https://sass-lang.com" target="_blank" rel="noopener noreferrer">Sass</a> string to CSS.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ variable|scss_to_css }}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">apply</span></span> scss_to_css %}</span><span class="xml">
</span><span class="hljs-comment">{# SCSS here #}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endapply</span></span> %}</span></code></pre>
<p>Alias: <code translate="no">sass_to_css</code>.</p>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> scss = 'some SCSS here' %}</span><span class="xml">
</span><span class="hljs-template-variable">{{ scss|scss_to_css }}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">apply</span></span> scss_to_css %}</span><span class="xml">
  $color: #fcfcfc;
  div {
    color: lighten($color, 20%);
  }
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endapply</span></span> %}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span></code></pre>
<h2 id="to-css">to_css</h2>
<p>Compiles a <a href="https://sass-lang.com" target="_blank" rel="noopener noreferrer">Sass</a> file to CSS.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(path)|to_css }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ path|to_css }}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset('styles.scss')|to_css }}</span></code></pre>
<h2 id="inline">inline</h2>
<p>Outputs the content of an <em>Asset</em>.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(path)|inline }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset('styles.css')|inline }}</span></code></pre>
<h2 id="dataurl">dataurl</h2>
<p>Returns the <a href="https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs" target="_blank" rel="noopener noreferrer">data URL</a> of an asset.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ asset(path)|dataurl }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ asset(image_path)|dataurl }}</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/languages/</id>
    <title>Languages</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/languages/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Languages</h1>
<h2 id="language">language</h2>
<p>The main language, defined by its code.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">language:</span> <span class="hljs-string">&lt;code&gt;</span> <span class="hljs-comment"># unique code (`en` by default)</span></code></pre>
<p>By default only others <a href="#languages">languages</a> pages path are prefixed with its language code, but you can prefix the path of the main language pages with the following option:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-comment">#language: &lt;code&gt;</span>
<span class="hljs-attr">language:</span>
  <span class="hljs-attr">code:</span> <span class="hljs-string">&lt;code&gt;</span>
  <span class="hljs-attr">prefix:</span> <span class="hljs-literal">true</span></code></pre>
<aside class="note note-info"><p>When <code translate="no">prefix</code> is set to <code translate="no">true</code>, an alias is automatically created for the home page that redirect from<code translate="no">/</code> to <code translate="no">/&lt;code&gt;/</code>.</p></aside>
<h2 id="languages">languages</h2>
<p>Options of available languages, used for <a href="/content/multilingual/">pages</a> and <a href="/templates/localization/">templates</a> localization.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">languages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">code:</span> <span class="hljs-string">&lt;code&gt;</span>          <span class="hljs-comment"># unique code (e.g.: `en`, `fr`, 'en-US', `fr-CA`)</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">&lt;name&gt;</span>          <span class="hljs-comment"># human readable name (e.g.: `Français`)</span>
    <span class="hljs-attr">locale:</span> <span class="hljs-string">&lt;locale&gt;</span>      <span class="hljs-comment"># locale code (`language_COUNTRY`, e.g.: `en_US`, `fr_FR`, `fr_CA`)</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-string">&lt;true|false&gt;</span> <span class="hljs-comment"># enabled or not (`true` by default)</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">language:</span> <span class="hljs-string">en</span>
<span class="hljs-attr">languages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">code:</span> <span class="hljs-string">en</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">English</span>
    <span class="hljs-attr">locale:</span> <span class="hljs-string">en_US</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">code:</span> <span class="hljs-string">fr</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Français</span>
    <span class="hljs-attr">locale:</span> <span class="hljs-string">fr_FR</span></code></pre>
<aside class="note note-info"><p>A <a href="/documentation/locale-codes/">locale code list</a> is available if needed.</p></aside>
<h3 id="localize">Localize</h3>
<p>To localize configuration options you must store them under the <code translate="no">config</code> key of the language.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">title:</span> <span class="hljs-string">"Cecil in english"</span>
<span class="hljs-attr">languages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">code:</span> <span class="hljs-string">en</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">English</span>
    <span class="hljs-attr">locale:</span> <span class="hljs-string">en_US</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">code:</span> <span class="hljs-string">fr</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Français</span>
    <span class="hljs-attr">locale:</span> <span class="hljs-string">fr_FR</span>
    <span class="hljs-attr">config:</span>
      <span class="hljs-attr">title:</span> <span class="hljs-string">"Cecil en français"</span></code></pre>
<aside class="note note-info"><p>In <a href="/templates/">templates</a> you can access to an option with <code translate="no">{{ site.&lt;option&gt; }}</code>, for example <code translate="no">{{ site.title }}</code>.<br>
If an option is not available in the current language (e.g.: <code translate="no">fr</code>) it fallback to the global one (e.g.: <code translate="no">en</code>).</p></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/content/front-matter/</id>
    <title>Front matter</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-03T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/content/front-matter/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Front matter</h1>
<p>The <em>front matter</em> can contains custom variables applied to the current page.</p>
<p>It must be the first thing in the file and must be a valid <a href="https://en.wikipedia.org/wiki/YAML" target="_blank" rel="noopener noreferrer">YAML</a>.</p>
<h2 id="predefined-variables">Predefined variables</h2>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
<th>Default value</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">title</code></td>
<td>Title</td>
<td>File name without extension.</td>
<td><code translate="no">Post 1</code></td>
</tr>
<tr>
<td><code translate="no">layout</code></td>
<td>Template</td>
<td>See <a href="/templates/lookup-rules/#lookup-rules"><em>Lookup rules</em></a>.</td>
<td><code translate="no">404</code></td>
</tr>
<tr>
<td><code translate="no">date</code></td>
<td>Creation date</td>
<td>File creation date (PHP <em>DateTime</em> object).</td>
<td><code translate="no">2019/04/15</code></td>
</tr>
<tr>
<td><code translate="no">section</code></td>
<td>Section</td>
<td>Page's <em>Section</em>.</td>
<td><code translate="no">blog</code></td>
</tr>
<tr>
<td><code translate="no">path</code></td>
<td>Path</td>
<td>Page's <em>path</em>.</td>
<td><code translate="no">blog/post-1</code></td>
</tr>
<tr>
<td><code translate="no">slug</code></td>
<td>Slug</td>
<td>Page's <em>slug</em>.</td>
<td><code translate="no">post-1</code></td>
</tr>
<tr>
<td><code translate="no">published</code></td>
<td>Published or not</td>
<td><code translate="no">true</code>.</td>
<td><code translate="no">false</code></td>
</tr>
<tr>
<td><code translate="no">draft</code></td>
<td>Published or not</td>
<td><code translate="no">false</code>.</td>
<td><code translate="no">true</code></td>
</tr>
</tbody>
</table>
<aside class="note note-info"><p>All the predefined variables can be overridden except <code translate="no">section</code>.</p></aside>
<h2 id="updated">updated</h2>
<p>The <code translate="no">updated</code> variable is used to define the last modification date of a page.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">updated:</span> <span class="hljs-number">2026</span><span class="hljs-number">-02</span><span class="hljs-number">-02</span>
<span class="hljs-meta">---</span></code></pre>
<aside class="note note-warning"><p>Before version 8.80.1, the <code translate="no">updated</code> variable was a predefined variable. It is now an optional variable (and must be defined in the front matter to be used).</p></aside>
<h2 id="menu">menu</h2>
<p>A page can be added to a <a href="/configuration/site/#menus">menu</a>.</p>
<p>The entry name is the page <code translate="no">title</code> and the URL is the page <code translate="no">path</code>.</p>
<p>The same page can be added to multiple menus, and each entry's position can be set with the <code translate="no">weight</code> key (lowest first). The <code translate="no">name</code> key can be used to override the default entry name per menu.</p>
<p><em>Examples:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">menu:</span> <span class="hljs-string">main</span>
<span class="hljs-meta">---</span></code></pre>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">menu:</span> <span class="hljs-string">[main,</span> <span class="hljs-string">navigation]</span> <span class="hljs-comment"># same page in multiple menus</span>
<span class="hljs-meta">---</span></code></pre>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">menu:</span>
  <span class="hljs-attr">main:</span>
    <span class="hljs-attr">weight:</span> <span class="hljs-number">10</span>
  <span class="hljs-attr">navigation:</span>
    <span class="hljs-attr">weight:</span> <span class="hljs-number">20</span>
<span class="hljs-meta">---</span></code></pre>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">'Our Expertise'</span>
<span class="hljs-attr">menu:</span>
  <span class="hljs-attr">main:</span>
    <span class="hljs-attr">weight:</span> <span class="hljs-number">15</span>
  <span class="hljs-attr">footer:</span>
    <span class="hljs-attr">weight:</span> <span class="hljs-number">15</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">"Expertise"</span> <span class="hljs-comment"># override the entry name in this menu</span>
<span class="hljs-meta">---</span></code></pre>
<h2 id="taxonomy">Taxonomy</h2>
<p>Taxonomy allows you to connect, relate and classify your website’s content.<br>
In Cecil, these terms are gathered within vocabularies.</p>
<p>Vocabularies are declared in the <a href="/configuration/site/#taxonomies"><em>Configuration</em></a>.</p>
<dl>
<dt>Vocabulary</dt>
<dd>A categorization of content (e.g.: <code translate="no">tags</code>, <code translate="no">categories</code>, etc.).</dd>
<dt>Term</dt>
<dd>A term is an item of a vocabulary (e.g.: <code translate="no">Development</code>, <code translate="no">PHP</code>, etc.).</dd>
</dl>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">tags:</span> <span class="hljs-string">["Development",</span> <span class="hljs-string">"PHP"</span><span class="hljs-string">]</span>
<span class="hljs-meta">---</span></code></pre>
<p>Cecil then generates, for each vocabulary:</p>
<ul>
<li>a page listing its terms, e.g.: <code translate="no">/tags/</code></li>
<li>a page per term listing its pages, e.g.: <code translate="no">/tags/development/</code> and <code translate="no">/tags/php/</code></li>
</ul>
<p>See <a href="/templates/lookup-rules/#type-vocabulary">templates lookup rules</a> and <a href="/templates/variables/#taxonomy">taxonomy variables</a> to customize those pages.</p>
<h2 id="schedule">Schedule</h2>
<p>Schedules pages’ publication.</p>
<p><em>Example:</em></p>
<p>The page will be published if current date is &gt;= 2023-02-07:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">schedule:</span>
  <span class="hljs-attr">publish:</span> <span class="hljs-number">2023</span><span class="hljs-number">-02</span><span class="hljs-number">-07</span></code></pre>
<p>This page is published if current date is &lt;= 2022-04-28:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">schedule:</span>
  <span class="hljs-attr">expiry:</span> <span class="hljs-number">2022</span><span class="hljs-number">-04</span><span class="hljs-number">-28</span></code></pre>
<h2 id="redirect">redirect</h2>
<p>As indicated by its name, the <code translate="no">redirect</code> variable is used to redirect a page to a dedicated URL.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">redirect:</span> <span class="hljs-string">"https://arnaudligny.fr"</span>
<span class="hljs-meta">---</span></code></pre>
<aside class="note note-info"><p>Redirect works with the <a href="https://github.com/Cecilapp/Cecil/blob/main/resources/layouts/_default/redirect.html.twig" target="_blank" rel="noopener noreferrer"><code translate="no">redirect.html.twig</code></a> template.</p></aside>
<h2 id="alias">alias</h2>
<p>Alias is a redirection to the current page</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">title:</span> <span class="hljs-string">"About"</span>
<span class="hljs-attr">alias:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">contact</span>
<span class="hljs-meta">---</span></code></pre>
<p>In the previous example <code translate="no">contact/</code> redirects to <code translate="no">about/</code>.</p>
<h2 id="output">output</h2>
<p>Defines the output format of the page.</p>
<p>Available formats are: <code translate="no">html</code>, <code translate="no">atom</code>, <code translate="no">rss</code>, <code translate="no">json</code>, <code translate="no">xml</code>, etc.<br>
You can define one or more formats in an array.</p>
<p>I’s not required to define an output format, but if you do, it must be one of the available formats defined in the <a href="/configuration/output/#output-formats"><em>Configuration</em></a>.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">output:</span> <span class="hljs-string">[html,</span> <span class="hljs-string">atom]</span>
<span class="hljs-meta">---</span></code></pre>
<h2 id="external">external</h2>
<p>A page with an <code translate="no">external</code> variable try to fetch the content of the pointed resource.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">external:</span> <span class="hljs-string">"https://raw.githubusercontent.com/Cecilapp/Cecil/main/README.md"</span>
<span class="hljs-meta">---</span></code></pre>
<h2 id="excluded">excluded</h2>
<p>Set <code translate="no">excluded</code> to <code translate="no">true</code> to hide a page from list pages (i.e.: <em>Home page</em>, <em>Section</em>, <em>Sitemap</em>, etc.).</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">excluded:</span> <span class="hljs-literal">true</span>
<span class="hljs-meta">---</span></code></pre>
<aside class="note note-info"><p><code translate="no">excluded</code> is different from <a href="#predefined-variables"><code translate="no">published</code></a>: an excluded page is published but hidden from list pages.</p></aside>
<aside class="note note-warning"><p>Since version 8.49.0, the previous <code translate="no">exclude</code> variable have been changed to <code translate="no">excluded</code>.</p></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/templates/variables/</id>
    <title>Variables</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-06T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/templates/variables/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Variables</h1>
<blockquote>
<p>The application passes variables to the templates for manipulation in the template. Variables may have attributes or elements you can access, too.<br>
Use a dot (.) to access attributes of a variable: <code translate="no">{{ foo.bar }}</code></p>
</blockquote>
<p>You can use variables from different scopes: <a href="#site"><code translate="no">site</code></a>, <a href="#page"><code translate="no">page</code></a>, <a href="#cecil"><code translate="no">cecil</code></a>.</p>
<h2 id="site">site</h2>
<p>The <code translate="no">site</code> variable contains built-in variables <strong>and</strong> those set in the <a href="/configuration/">configuration</a>.</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">site.pages</code></td>
<td>Collection of all pages, in the current language.</td>
</tr>
<tr>
<td><code translate="no">site.allpages</code></td>
<td>Collection of all pages, in all languages.</td>
</tr>
<tr>
<td><code translate="no">site.page(id)</code></td>
<td>A page with the given ID.</td>
</tr>
<tr>
<td><code translate="no">site.taxonomies</code></td>
<td>Collection of vocabularies.</td>
</tr>
<tr>
<td><code translate="no">site.home</code></td>
<td>ID of the home page.</td>
</tr>
<tr>
<td><code translate="no">site.time</code></td>
<td>Current <a href="https://wikipedia.org/wiki/Unix_time" target="_blank" rel="noopener noreferrer"><em>Timestamp</em></a>.</td>
</tr>
<tr>
<td><code translate="no">site.debug</code></td>
<td>Debug mode status (<code translate="no">true</code> or <code translate="no">false</code>).</td>
</tr>
<tr>
<td><code translate="no">site.build</code></td>
<td>Current build ID.</td>
</tr>
</tbody>
</table>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">title:</span> <span class="hljs-string">"My amazing website!"</span></code></pre>
<p>Can be displayed in a template with:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ site.title }}</span></code></pre>
<aside class="note note-important"><p>Use <code translate="no">showable</code> method on pages collection to return only published and not <em>virtual/redirect/excluded</em> pages.</p>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> page in site.pages.showable %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ page.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span></code></pre></aside>
<aside class="note note-warning"><p>In some cases, you can encounter conflicts between configuration and built-in variables (e.g. <code translate="no">pages.default</code> configuration). In that case, you can use <code translate="no">config.&lt;variable&gt;</code> (where <code translate="no">&lt;variable&gt;</code> is the variable name/path) to access the raw configuration directly.</p>
<p>Example:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ config.pages.default.sitemap.priority }}</span></code></pre></aside>
<h3 id="site-menus">site.menus</h3>
<p>Loop on <code translate="no">site.menus.&lt;menu&gt;</code> to get each entry of the <code translate="no">&lt;menu&gt;</code> collection (e.g.: <code translate="no">main</code>).</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">&lt;entry&gt;.name</code></td>
<td>Entry name.</td>
</tr>
<tr>
<td><code translate="no">&lt;entry&gt;.url</code></td>
<td>Entry URL.</td>
</tr>
<tr>
<td><code translate="no">&lt;entry&gt;.weight</code></td>
<td>Entry weight (useful to sort menu entries).</td>
</tr>
</tbody>
</table>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ol</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> entry in site.menus.main|sort_by_weight %}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(entry.url) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">data-weight</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ entry.weight }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ entry.name }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span><span class="xml">
  <span class="hljs-tag">&lt;/<span class="hljs-name">ol</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span></code></pre>
<h3 id="site-language">site.language</h3>
<p>Information about the current language.</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">site.language</code></td>
<td>Language code (e.g.: <code translate="no">en</code>).</td>
</tr>
<tr>
<td><code translate="no">site.language.name</code></td>
<td>Language name (e.g.: <code translate="no">English</code>).</td>
</tr>
<tr>
<td><code translate="no">site.language.locale</code></td>
<td>Language <a href="/configuration/locale-codes/">locale code</a> (e.g.: <code translate="no">en_US</code>).</td>
</tr>
<tr>
<td><code translate="no">site.language.weight</code></td>
<td>Language position in the <code translate="no">languages</code> list.</td>
</tr>
</tbody>
</table>
<aside class="note note-tip"><p>You can retrieve <code translate="no">name</code>, <code translate="no">locale</code> and <code translate="no">weight</code> of a specific language by passing its code as a parameter.<br>
e.g.: <code translate="no">site.language.name('fr')</code>.</p></aside>
<h3 id="site-static">site.static</h3>
<p>The static files collection can be accessed via <code translate="no">site.static</code> if the <a href="/configuration/data-static/#static-load"><em>static load</em></a> is enabled.</p>
<p>Each file exposes the following properties:</p>
<ul>
<li><code translate="no">path</code>: relative path (e.g.: <code translate="no">/images/img-1.jpg</code>)</li>
<li><code translate="no">date</code>: creation date (<em>timestamp</em>)</li>
<li><code translate="no">updated</code>: modification date (<em>timestamp</em>)</li>
<li><code translate="no">name</code>: name (e.g.: <code translate="no">img-1.jpg</code>)</li>
<li><code translate="no">basename</code>: name without extension (e.g.: <code translate="no">img-1</code>)</li>
<li><code translate="no">ext</code>: extension (e.g.: <code translate="no">jpg</code>)</li>
<li><code translate="no">type</code>: media type (e.g.: <code translate="no">image</code>)</li>
<li><code translate="no">subtype</code>: media sub type (e.g.: <code translate="no">image/jpeg</code>)</li>
<li><code translate="no">exif</code>: image EXIF data (<em>array</em>)</li>
<li><code translate="no">audio</code>: <a href="https://github.com/wapmorgan/Mp3Info#audio-information" target="_blank" rel="noopener noreferrer">Mp3Info</a> object</li>
<li><code translate="no">video</code>: array of basic video information (duration in seconds, width and height)</li>
</ul>
<h3 id="site-data">site.data</h3>
<p>A data collection can be accessed via <code translate="no">site.data.&lt;filename&gt;</code> (without file extension).</p>
<p><em>Examples:</em></p>
<ul>
<li><code translate="no">data/authors.yml</code> : <code translate="no">site.data.authors</code></li>
<li><code translate="no">data/authors.fr.yml</code> : <code translate="no">site.data.authors</code> (if <code translate="no">site.language</code> = "fr")</li>
<li><code translate="no">data/galleries/gallery-1.json</code> : <code translate="no">site.data.galleries['gallery-1']</code></li>
</ul>
<h2 id="page">page</h2>
<p>The <code translate="no">page</code> variable contains built-in variables of a page <strong>and</strong> those set in the <a href="/content/pages/#front-matter">front matter</a>.</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">page.id</code></td>
<td>Unique identifier.</td>
<td><code translate="no">blog/post-1</code></td>
</tr>
<tr>
<td><code translate="no">page.title</code></td>
<td>File name (without extension).</td>
<td><code translate="no">Post 1</code></td>
</tr>
<tr>
<td><code translate="no">page.date</code></td>
<td>File creation date.</td>
<td><em>DateTime</em></td>
</tr>
<tr>
<td><code translate="no">page.body</code></td>
<td>File body.</td>
<td><em>Markdown</em></td>
</tr>
<tr>
<td><code translate="no">page.content</code></td>
<td>File body converted in HTML.</td>
<td><em>HTML</em></td>
</tr>
<tr>
<td><code translate="no">page.section</code></td>
<td>File root folder (<em>slugified</em>).</td>
<td><code translate="no">blog</code></td>
</tr>
<tr>
<td><code translate="no">page.path</code></td>
<td>File path (<em>slugified</em>).</td>
<td><code translate="no">blog/post-1</code></td>
</tr>
<tr>
<td><code translate="no">page.slug</code></td>
<td>File name (<em>slugified</em>).</td>
<td><code translate="no">post-1</code></td>
</tr>
<tr>
<td><code translate="no">page.filepath</code></td>
<td>File system path.</td>
<td><code translate="no">Blog/Post 1.md</code></td>
</tr>
<tr>
<td><code translate="no">page.type</code></td>
<td><code translate="no">homepage</code>, <code translate="no">page</code>, <code translate="no">section</code>, <code translate="no">vocabulary</code> or <code translate="no">term</code>.</td>
<td><code translate="no">page</code></td>
</tr>
<tr>
<td><code translate="no">page.pages</code></td>
<td>Collection of all sub pages.</td>
<td><em>Collection</em></td>
</tr>
<tr>
<td><code translate="no">page.translations</code></td>
<td>Collection of translated pages.</td>
<td><em>Collection</em></td>
</tr>
</tbody>
</table>
<aside class="note note-important"><p>Use <code translate="no">showable</code> method on pages collection to return only published and not <em>virtual/redirect/excluded</em> pages.</p>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> page in page.pages.showable %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ page.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span></code></pre></aside>
<h3 id="nested-sections">Nested sections</h3>
<p>In a <a href="/content/pages/#sub-section">nested sections</a> context, <code translate="no">page.parent</code>, <code translate="no">page.ancestors</code>, <code translate="no">page.sections</code> and <code translate="no">page.toplevel</code> help you build navigation.</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">page.parent</code></td>
<td>Parent <em>section</em>'s page (<code translate="no">null</code> if none).</td>
<td><em>Page</em></td>
</tr>
<tr>
<td><code translate="no">page.ancestors</code></td>
<td>Collection of ancestor <em>sections</em> (nearest first).</td>
<td><em>Collection</em></td>
</tr>
<tr>
<td><code translate="no">page.sections</code></td>
<td>Collection of immediate descendant <em>sections</em>.</td>
<td><em>Collection</em></td>
</tr>
<tr>
<td><code translate="no">page.toplevel</code></td>
<td><code translate="no">true</code> if the page is a top level <em>section</em>.</td>
<td><em>Boolean</em></td>
</tr>
</tbody>
</table>
<p><em>Breadcrumb (from the home page to the current page):</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"breadcrumb"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(site.home) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ site.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> section in page.ancestors|<span class="hljs-keyword">reverse</span> %}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(section) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ section.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span><span class="xml">
    </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.id != site.home %}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span> <span class="hljs-attr">aria-current</span>=<span class="hljs-string">"page"</span>&gt;</span></span><span class="hljs-template-variable">{{ page.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span><span class="xml">
  <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span></code></pre>
<aside class="note note-tip"><p>A ready-to-use <a href="https://github.com/Cecilapp/Cecil/blob/main/resources/layouts/partials/breadcrumb.html.twig" target="_blank" rel="noopener noreferrer"><code translate="no">breadcrumb.html.twig</code></a> partial is available:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ <span class="hljs-name">include</span><span class="hljs-params">('partials/breadcrumb.html.twig')</span> }}</span></code></pre></aside>
<p><em>Sub-sections menu (immediate descendant sections of the current section):</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.sections|<span class="hljs-keyword">length</span> %}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> section in page.sections|sort_by_title %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(section) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ section.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span></code></pre>
<p><em>Main navigation limited to top level sections (from any page):</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> section in site.page(site.home).sections|sort_by_title %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(section) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ section.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span></code></pre>
<p><em>Link to the parent section:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.<span class="hljs-name">parent</span> %}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.<span class="hljs-name">parent</span>) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span>← </span><span class="hljs-template-variable">{{ page.<span class="hljs-name">parent</span>.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span></code></pre>
<h3 id="page-prev-next">page.&lt;prev/next&gt;</h3>
<p>Navigation between pages within the same <em>section</em>, sorted according to the section's <code translate="no">sortby</code> (chronological order for dates).</p>
<p>With <a href="/content/pages/#sub-section">sub-sections</a>, navigation follows the sections tree: the pages of a top level <em>Section</em> and of all its sub-sections are chained, each sub-section (its index page) being placed among the pages of its parent <em>Section</em> and followed by its own pages.</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">page.prev</code></td>
<td>Previous page.</td>
<td><em>Page</em></td>
</tr>
<tr>
<td><code translate="no">page.next</code></td>
<td>Next page.</td>
<td><em>Page</em></td>
</tr>
</tbody>
</table>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.prev) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ page.prev.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span></code></pre>
<h3 id="page-paginator">page.paginator</h3>
<p><em>Paginator</em> helps you build navigation for list pages: homepage, sections, and taxonomies.</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">page.paginator.pages</code></td>
<td>Pages Collection.</td>
</tr>
<tr>
<td><code translate="no">page.paginator.pages_total</code></td>
<td>Number total of pages.</td>
</tr>
<tr>
<td><code translate="no">page.paginator.count</code></td>
<td>Number of paginator's pages.</td>
</tr>
<tr>
<td><code translate="no">page.paginator.current</code></td>
<td>Position index of the current page.</td>
</tr>
<tr>
<td><code translate="no">page.paginator.links.first</code></td>
<td>Page ID of the first page.</td>
</tr>
<tr>
<td><code translate="no">page.paginator.links.prev</code></td>
<td>Page ID of the previous page.</td>
</tr>
<tr>
<td><code translate="no">page.paginator.links.self</code></td>
<td>Page ID of the current page.</td>
</tr>
<tr>
<td><code translate="no">page.paginator.links.next</code></td>
<td>Page ID of the next page.</td>
</tr>
<tr>
<td><code translate="no">page.paginator.links.last</code></td>
<td>Page ID of the last page.</td>
</tr>
<tr>
<td><code translate="no">page.paginator.links.path</code></td>
<td>Page ID without the position index.</td>
</tr>
</tbody>
</table>
<aside class="note note-important"><p>Because links entries are Page ID you must use the <code translate="no">url()</code> function to create working links.<br>
e.g: <code translate="no">{{ url(page.paginator.links.next) }}</code></p></aside>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.paginator %}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.paginator.links.prev is defined %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.paginator.links.prev) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span>Previous<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span><span class="xml">
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.paginator.links.next is defined %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.paginator.links.next) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span>Next<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> page.paginator %}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> paginator_index in 1..page.paginator.count %}</span><span class="xml">
    </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> paginator_index != page.paginator.current %}</span><span class="xml">
      </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">if</span></span> paginator_index == 1 %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.paginator.links.first) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ paginator_index }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      </span><span class="hljs-template-tag">{% <span class="hljs-name">else</span> %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.paginator.links.path ~ '/' ~ paginator_index) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ paginator_index }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span><span class="xml">
    </span><span class="hljs-template-tag">{% <span class="hljs-name">else</span> %}</span><span class="xml">
  </span><span class="hljs-template-variable">{{ paginator_index }}</span><span class="xml">
    </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span><span class="xml">
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endif</span></span> %}</span></code></pre>
<h3 id="taxonomy">Taxonomy</h3>
<p>Variables available in <em>vocabulary</em> and <em>term</em> templates.</p>
<h4>Vocabulary</h4>
<p>Page <code translate="no">/&lt;plural&gt;/</code> (e.g.: <code translate="no">/categories/</code>).</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">page.plural</code></td>
<td>Vocabulary name in plural form.</td>
</tr>
<tr>
<td><code translate="no">page.singular</code></td>
<td>Vocabulary name in singular form.</td>
</tr>
<tr>
<td><code translate="no">page.terms</code></td>
<td>List of terms (<em>Collection</em>).</td>
</tr>
</tbody>
</table>
<p>Each term of <code translate="no">page.terms</code> provides <code translate="no">term.id</code> (term ID, e.g.: <code translate="no">categories/php</code>), <code translate="no">term.name</code> (term name, e.g.: <code translate="no">PHP</code>) and the number of its pages with <code translate="no">term|length</code>.</p>
<h4>Term</h4>
<p>Page <code translate="no">/&lt;plural&gt;/&lt;term&gt;/</code> (e.g.: <code translate="no">/categories/php/</code>).</p>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">page.title</code></td>
<td>Term name.</td>
</tr>
<tr>
<td><code translate="no">page.term</code></td>
<td>Term ID (e.g.: <code translate="no">categories/php</code>).</td>
</tr>
<tr>
<td><code translate="no">page.plural</code></td>
<td>Vocabulary name in plural form.</td>
</tr>
<tr>
<td><code translate="no">page.singular</code></td>
<td>Vocabulary name in singular form.</td>
</tr>
<tr>
<td><code translate="no">page.pages</code></td>
<td>List of pages in this term, sorted by date (<em>Collection</em>).</td>
</tr>
</tbody>
</table>
<h4>Taxonomy example</h4>
<p>Configuration:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">taxonomies:</span>
  <span class="hljs-attr">categories:</span> <span class="hljs-string">category</span></code></pre>
<p>Page front matter:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">categories:</span> <span class="hljs-string">["Data</span> <span class="hljs-string">Sovereignty"]</span>
<span class="hljs-meta">---</span></code></pre>
<p>List of terms (<code translate="no">/categories/</code>), in <code translate="no">layouts/taxonomy/categories.html.twig</code>:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">extends</span></span> 'page.html.twig' %}</span><span class="xml">

</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">block</span></span> content %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="hljs-template-variable">{{ page.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> term in page.terms %}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(term.id) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ term.name }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> (</span><span class="hljs-template-variable">{{ term|<span class="hljs-keyword">length</span> }}</span><span class="xml">)<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span><span class="xml">
  <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endblock</span></span> %}</span></code></pre>
<p>List of pages of a term (<code translate="no">/categories/data-sovereignty/</code>), in <code translate="no">layouts/taxonomy/category.html.twig</code>:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">extends</span></span> 'page.html.twig' %}</span><span class="xml">

</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">block</span></span> content %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="hljs-template-variable">{{ page.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> p in page.paginator.pages ?? page.pages %}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(p) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ p.title }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
  </span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url(page.plural) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span>All </span><span class="hljs-template-variable">{{ page.plural }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endblock</span></span> %}</span></code></pre>
<p>Links to the terms of the current page, in a page template:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">for</span></span> category in page.categories ?? [] %}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{{ url('categories/' ~ category) }}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">"</span>&gt;</span></span><span class="hljs-template-variable">{{ category }}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endfor</span></span> %}</span></code></pre>
<aside class="note note-tip"><p>The <a href="/documentation/reference/functions/#url"><code translate="no">url()</code></a> function slugifies the given string to find the matching page: <code translate="no">url('categories/Data Sovereignty')</code> returns <code translate="no">/categories/data-sovereignty/</code>.</p>
<p>You can also use the built-in partial <code translate="no">{{ include('partials/terms-list.html.twig', {vocabulary: 'categories'}) }}</code>.</p></aside>
<h2 id="cecil">cecil</h2>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code translate="no">cecil.url</code></td>
<td>URL of the Cecil website.</td>
</tr>
<tr>
<td><code translate="no">cecil.version</code></td>
<td>Cecil current version.</td>
</tr>
<tr>
<td><code translate="no">cecil.poweredby</code></td>
<td>Print <code translate="no">Cecil v%s</code>, with <code translate="no">%s</code> is the current version.</td>
</tr>
</tbody>
</table>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/templates/reference/sorts/</id>
    <title>Sorts</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/templates/reference/sorts/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Sorts</h1>
<p>Sorting collections (of pages, menus or taxonomies).</p>
<h2 id="sort-by-title">sort_by_title</h2>
<p>Sorts a collection by title (with <a href="https://en.wikipedia.org/wiki/Natural_sort_order" target="_blank" rel="noopener noreferrer">natural sort</a>).</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ collection|sort_by_title }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ site.pages|sort_by_title }}</span></code></pre>
<h2 id="sort-by-date">sort_by_date</h2>
<p>Sorts a collection by date (most recent first).</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ collection|sort_by_date(variable='<span class="hljs-name">date</span>', desc_title=false) }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-comment">{# sort by date #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ site.pages|sort_by_date }}</span><span class="xml">
</span><span class="hljs-comment">{# sort by updated variable instead of date #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ site.pages|sort_by_date(variable='updated') }}</span><span class="xml">
</span><span class="hljs-comment">{# sort items with the same date by desc title #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ site.pages|sort_by_date(desc_title=true) }}</span><span class="xml">
</span><span class="hljs-comment">{# reverse sort #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ site.pages|sort_by_date|<span class="hljs-keyword">reverse</span> }}</span></code></pre>
<h2 id="sort-by-weight">sort_by_weight</h2>
<p>Sorts a collection by weight (lighter first).</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ collection|sort_by_weight }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ site.menus.main|sort_by_weight }}</span></code></pre>
<h2 id="sort">sort</h2>
<p>For more complex cases, you should use <a href="https://twig.symfony.com/doc/filters/sort.html" target="_blank" rel="noopener noreferrer">Twig’s native <code translate="no">sort</code></a>.</p>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> files = site.static|<span class="hljs-keyword">sort</span>((a, b) =&gt; a.<span class="hljs-name">date</span>|<span class="hljs-keyword">date</span>('U') &lt; b.<span class="hljs-name">date</span>|<span class="hljs-keyword">date</span>('U')) %}</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/content/markdown/</id>
    <title>Markdown</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-03T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/content/markdown/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Markdown</h1>
<p>Cecil supports <a href="http://daringfireball.net/projects/markdown/syntax" target="_blank" rel="noopener noreferrer">Markdown</a> format, but also <a href="https://michelf.ca/projects/php-markdown/extra/" target="_blank" rel="noopener noreferrer">Markdown Extra</a>.</p>
<p>Cecil also provides <strong>extra features</strong> to enhance your content, see below.</p>
<h2 id="attributes">Attributes</h2>
<p>With <a href="https://michelf.ca/projects/php-markdown/extra/" target="_blank" rel="noopener noreferrer">Markdown Extra</a> you can set an id, class and custom attributes on certain elements using an attribute block.<br>
For instance, put the desired attribute(s) after a header, a fenced code block, a link or an image at the end of the line inside curly brackets, like this:</p>
<pre><code class="language-markdown hljs markdown" translate="no"><span class="hljs-section">## Header {#id .class attribute=value}</span></code></pre>
<aside class="note note-warning"><p>For an inline element, like a link, you must use a line break after the closing brace:</p>
<pre><code class="language-markdown hljs markdown" translate="no">Lorem ipsum [<span class="hljs-string">dolor</span>](<span class="hljs-link">url</span>){attribute=value} 
sit amet.</code></pre></aside>
<h2 id="links">Links</h2>
<p>You can create a link with the syntax <code translate="no">[Text](url)</code>, where <code translate="no">url</code> can be a path, a relative path to a Markdown file, an external URL, etc.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">[<span class="hljs-string">Link to a path</span>](<span class="hljs-link">/about/</span>)
[<span class="hljs-string">Link to a Markdown file</span>](<span class="hljs-link">/about/</span>)
[<span class="hljs-string">Link to Cecil website</span>](<span class="hljs-link">https://cecil.app</span>)</code></pre>
<aside class="note note-info"><p>A relative link to a Markdown file is resolved from the folder of the current file (as on GitHub), then replaced by the URL of the targeted page.</p></aside>
<h3 id="link-to-a-page">Link to a page</h3>
<p>You can easily create a link to a page with the syntax <code translate="no">[Page title](page:page-id)</code>.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">[<span class="hljs-string">Link to a blog post</span>](<span class="hljs-link">page:blog/post-1</span>)</code></pre>
<h3 id="external">External</h3>
<p>By default external links have the following value for <code translate="no">rel</code> attribute: <code translate="no">noopener noreferrer</code>.</p>
<p><em>Example:</em></p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"&lt;url&gt;"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>&gt;</span>Link to another website<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></code></pre>
<p>You can change this behavior with <a href="/configuration/pages/#pages-body-links"><code translate="no">pages.body.links.external</code> options</a>.</p>
<h3 id="embedded-links">Embedded links</h3>
<p>Cecil can try to turn a link into embedded content by using the <code translate="no">{embed}</code> attribute or by setting the global configuration option <code translate="no">pages.body.links.embed.enabled</code> to <code translate="no">true</code>.</p>
<aside class="note note-important"><p>Only <strong>YouTube</strong>, <strong>Vimeo</strong>, <strong>Dailymotion</strong>, and <strong>GitHub Gists</strong> links are supported.</p></aside>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">[<span class="hljs-string">CECIL : LE générateur de SITES STATIQUES en PHP</span>](<span class="hljs-link">https://www.youtube.com/watch?v=ur8koU0iYvc</span>){embed}</code></pre>
<p><div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;">
<iframe src="https://www.youtube-nocookie.com/embed/ur8koU0iYvc" loading="lazy" width="640" height="360" frameborder="0" allow="accelerometer;autoplay;encrypted-media;gyroscope;picture-in-picture;fullscreen;web-share;" allowfullscreen="" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background-color:#d8d8d8;"></iframe>
</div></p>
<h4>Local video/audio files</h4>
<p>Cecil can also create a video and audio HTML elements, through the file extension.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">[<span class="hljs-string">Video file</span>](<span class="hljs-link">video.mp4</span>){embed controls poster=/images/video-test.png}
[<span class="hljs-string">Audio file</span>](<span class="hljs-link">song.mp3</span>){embed controls}</code></pre>
<p>Is converted to:</p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">video</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/video.mp4"</span> <span class="hljs-attr">controls</span> <span class="hljs-attr">poster</span>=<span class="hljs-string">"/images/video-test.png"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"max-width:100%;height:auto;"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">video</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">audio</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/song.mp3"</span> <span class="hljs-attr">controls</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">audio</span>&gt;</span></code></pre>
<h2 id="images">Images</h2>
<p>To add an image, use an exclamation mark (<code translate="no">!</code>) followed by alternative description in brackets (<code translate="no">[]</code>), and the path or URL to the image in parentheses (<code translate="no">()</code>).<br>
You can optionally add a title in quotation marks.</p>
<pre><code class="language-markdown hljs markdown" translate="no">![<span class="hljs-string">Alternative description</span>](<span class="hljs-link">/image.jpg "Image title"</span>)</code></pre>
<aside class="note note-info"><p>The path should be relative to the root of your website (e.g.: <code translate="no">/image.jpg</code>), however Cecil is able to normalize a path relative to <em>assets</em> and <em>static</em> directories (e.g.: <code translate="no">../../assets/image.jpg</code>).</p></aside>
<h3 id="lazy-loading">Lazy loading</h3>
<p>Cecil adds the attribute <code translate="no">loading="lazy"</code> to each image.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">![](/image.jpg)</code></pre>
<p>Is converted to:</p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/image.jpg"</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">"lazy"</span>&gt;</span></code></pre>
<aside class="note note-info"><p>You can disable this behavior with the attribute <code translate="no">{loading=eager}</code> or with the <a href="/configuration/pages/#pages-body-images"><code translate="no">lazy</code> option</a>.</p></aside>
<h3 id="decoding">Decoding</h3>
<p>Cecil adds the attribute <code translate="no">decoding="async"</code> to each image.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">![](/image.jpg)</code></pre>
<p>Is converted to:</p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/image.jpg"</span> <span class="hljs-attr">decoding</span>=<span class="hljs-string">"async"</span>&gt;</span></code></pre>
<aside class="note note-info"><p>You can disable this behavior with the attribute <code translate="no">{decoding=auto}</code> or with the <a href="/configuration/pages/#pages-body-images"><code translate="no">decoding</code> option</a>.</p></aside>
<h3 id="resize">Resize</h3>
<p>Each image in the <em>body</em> can be resized automatically by setting a smaller width than the original one, with the extra attribute <code translate="no">{width=X}</code>.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">![](/image.jpg){width=800}</code></pre>
<p>Is converted to:</p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/thumbnails/800/image.jpg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"800"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"600"</span>&gt;</span></code></pre>
<aside class="note note-info"><p>Ratio is preserved (<code translate="no">height</code> attribute is calculated automatically), the original file is not altered and the resized version is stored in <code translate="no">/thumbnails/&lt;width&gt;/</code>.</p></aside>
<aside class="note note-important"><p>This feature requires an image processing library: <a href="https://www.php.net/manual/book.imagick.php" target="_blank" rel="noopener noreferrer">Imagick</a> is used first if available (and able to read JPEG and PNG), then <a href="https://www.libvips.org/" target="_blank" rel="noopener noreferrer">libvips</a> (through the PHP <a href="https://www.php.net/manual/book.ffi.php" target="_blank" rel="noopener noreferrer">FFI</a> extension), and finally <a href="https://www.php.net/manual/book.image.php" target="_blank" rel="noopener noreferrer">GD</a> as fallback; otherwise it only adds a <code translate="no">width</code> HTML attribute to the <code translate="no">img</code> tag.</p></aside>
<aside class="note note-info"><p>libvips support is optional and is not bundled with <code translate="no">cecil.phar</code>. To use it, Cecil must be installed with <a href="https://getcomposer.org" target="_blank" rel="noopener noreferrer">Composer</a>, and you need:</p>
<ol>
<li><a href="https://www.libvips.org/install.html" target="_blank" rel="noopener noreferrer">libvips</a> installed on your system</li>
<li>the PHP <a href="https://www.php.net/manual/book.ffi.php" target="_blank" rel="noopener noreferrer">FFI</a> extension enabled</li>
<li>the <code translate="no">intervention/image-driver-vips</code> package installed alongside Cecil</li>
</ol>
<p>If Cecil is a dependency of your project (see <a href="/developers/library/#libvips-support">Library</a>):</p>
<pre><code class="language-bash hljs bash" translate="no">composer require intervention/image-driver-vips</code></pre>
<p>If Cecil is installed globally:</p>
<pre><code class="language-bash hljs bash" translate="no">composer global require cecil/cecil intervention/image-driver-vips</code></pre></aside>
<h3 id="formats">Formats</h3>
<p>If the <a href="/configuration/pages/#pages-body-images"><code translate="no">formats</code> option</a> is defined, alternatives images are created and added.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">![](/image.jpg)</code></pre>
<p>Could be converted to:</p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">picture</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">source</span> <span class="hljs-attr">srcset</span>=<span class="hljs-string">"/image.avif"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/avif"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">source</span> <span class="hljs-attr">srcset</span>=<span class="hljs-string">"/image.webp"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"image/webp"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/image.jpg"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">picture</span>&gt;</span></code></pre>
<aside class="note note-important"><p>Please note that <strong>not all image formats</strong> are always included in the PHP image extensions.</p></aside>
<h3 id="responsive">Responsive</h3>
<p>If the <a href="/configuration/pages/#pages-body-images"><code translate="no">responsive</code> option</a> is enabled, then all images in the <em>body</em> will be made responsive automatically.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">![](/image.jpg){width=800}</code></pre>
<p>will be converted to:</p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/thumbnails/800/image.jpg"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"800"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"600"</span>
  <span class="hljs-attr">srcset</span>=<span class="hljs-string">"/thumbnails/320/image.jpg 320w,
          /thumbnails/640/image.jpg 640w,
          /thumbnails/800/image.jpg 800w"</span>
  <span class="hljs-attr">sizes</span>=<span class="hljs-string">"100vw"</span>
&gt;</span></code></pre>
<aside class="note note-info"><p>Because a body image is converted into an <a href="/assets/#asset">Asset</a>, the different widths must be defined in <a href="/configuration/assets/">assets configuration</a>.</p></aside>
<p>The <code translate="no">sizes</code> attribute takes the value of the <code translate="no">assets.images.responsive.sizes.default</code> configuration option, but it can be changed by creating a new entry named after a <em>class</em> added to the image.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">responsive:</span>
      <span class="hljs-attr">sizes:</span>
        <span class="hljs-attr">default:</span> <span class="hljs-string">100vw</span>
        <span class="hljs-attr">my_class:</span> <span class="hljs-string">"(max-width: 800px) 768px, 1024px"</span></code></pre>
<pre><code class="language-markdown hljs markdown" translate="no">![](/image.jpg){.my_class}</code></pre>
<aside class="note note-info"><p>You can combine <code translate="no">formats</code> and <code translate="no">responsive</code> options.</p></aside>
<h3 id="css-class">CSS class</h3>
<p>You can set a default value to the <code translate="no">class</code> attribute of each image with the <a href="/configuration/pages/#pages-body-images"><code translate="no">class</code> option</a>.</p>
<h3 id="caption">Caption</h3>
<p>The optional title can be used to create a caption (<code translate="no">figcaption</code>) automatically by enabling the <a href="/configuration/pages/#pages-body-images"><code translate="no">caption</code> option</a>.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">![](/images/img.jpg "Title")</code></pre>
<p>Is converted to:</p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">figure</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/image.jpg"</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Title"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">figcaption</span>&gt;</span>Title<span class="hljs-tag">&lt;/<span class="hljs-name">figcaption</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span></code></pre>
<aside class="note note-info"><p>Caption supports Markdown content.</p></aside>
<h3 id="localized-image">Localized image</h3>
<p>For translated pages, Cecil first looks for a language-suffixed file when resolving Markdown image paths.</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">![](/images/cecil-logo.png)</code></pre>
<p>With a French page (<code translate="no">fr</code>), Cecil tries <code translate="no">/images/cecil-logo.fr.png</code> first, then falls back to <code translate="no">/images/cecil-logo.png</code>.</p>
<h3 id="placeholder">Placeholder</h3>
<p>As images are typically heavier and slower resources, and they don’t block rendering, we should attempt to give users something to look at while they wait for the image to arrive.</p>
<p>The <code translate="no">placeholder</code> attribute accepts two options:</p>
<ol>
<li><code translate="no">color</code>: display a colored background (based on image dominant color)</li>
<li><code translate="no">lqip</code>: <a href="https://www.guypo.com/introducing-lqip-low-quality-image-placeholders" target="_blank" rel="noopener noreferrer">Low-Quality Image Placeholder</a></li>
</ol>
<p><em>Examples:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">![](/images/img.jpg){placeholder=color}
![](/images/img.jpg){placeholder=lqip}</code></pre>
<aside class="note note-tip"><p>You can set a value to the <code translate="no">placeholder</code> attribute for each image with the <a href="/configuration/pages/#pages-body-images"><code translate="no">placeholder</code> option</a>.</p></aside>
<aside class="note note-warning"><p>The <code translate="no">lqip</code> option is not compatible with animated GIF.</p></aside>
<h2 id="table-of-contents">Table of contents</h2>
<p>You can add a table of contents with the following Markdown syntax:</p>
<pre><code class="language-markdown hljs markdown" translate="no">[toc]</code></pre>
<aside class="note note-info"><p>By default, the ToC extracts H2 and H3 headings. You can change this behavior with <a href="/configuration/pages/#pages-body">body options</a>.</p></aside>
<h2 id="excerpt">Excerpt</h2>
<p>An excerpt can be defined in the <em>body</em> with one of those following tags: <code translate="no">excerpt</code> or <code translate="no">break</code>.</p>
<p><em>Example:</em></p>
<pre><code class="language-html hljs xml" translate="no">Introduction.
<span class="hljs-comment">&lt;!-- excerpt --&gt;</span>
Main content.</code></pre>
<p>Then use the <a href="/templates/reference/filters/#excerpt-html"><code translate="no">excerpt_html</code> filter</a> in your template.</p>
<h2 id="notes">Notes</h2>
<p>Create a <em>Note</em> block (info, tips, important, etc.).</p>
<p><em>Example:</em></p>
<pre><code class="language-markdown hljs markdown" translate="no">:::tip
<span class="hljs-strong">**Tip:**</span> This is advice.
:::</code></pre>
<p>Is converted to:</p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">aside</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"note note-tip"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Tip:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> This is advice.
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">aside</span>&gt;</span></code></pre>
<aside class="note note-tip"><p><strong>Tip:</strong> This is advice.</p></aside>
<p><em>Others examples:</em></p>
<aside class="note"><p>empty</p></aside>
<aside class="note note-info"><p>info</p></aside>
<aside class="note note-tip"><p>tip</p></aside>
<aside class="note note-important"><p>important</p></aside>
<aside class="note note-warning"><p>warning</p></aside>
<aside class="note note-caution"><p>caution</p></aside>
<h2 id="syntax-highlight">Syntax highlight</h2>
<p>Code block syntax highlighting is enabled by default with the <a href="/configuration/pages/#pages-body-highlight">pages.body.highlight</a> option.</p>
<p>If needed, you can disable it with:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">body:</span>
    <span class="hljs-attr">highlight:</span> <span class="hljs-literal">false</span></code></pre>
<p><em>Example:</em></p>
<pre>
```php
echo "Hello world";
```
</pre>
<p>Is rendered to:</p>
<pre><code class="language-php hljs php" translate="no"><span class="hljs-keyword">echo</span> <span class="hljs-string">"Hello world"</span>;</code></pre>
<aside class="note note-info"><p>You can customize the syntax highlighting style by creating your own theme. See the <a href="https://highlightjs.readthedocs.io/en/latest/theme-guide.html" target="_blank" rel="noopener noreferrer">Highlight.js Theme Guide</a>.</p></aside>
<h2 id="inserted-text">Inserted text</h2>
<p>Represents a range of text that has been added.</p>
<pre><code class="language-markdown hljs markdown" translate="no">++text++</code></pre>
<p>Is converted to:</p>
<pre><code class="language-html hljs xml" translate="no"><span class="hljs-tag">&lt;<span class="hljs-name">ins</span>&gt;</span>text<span class="hljs-tag">&lt;/<span class="hljs-name">ins</span>&gt;</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/getting-started/directory-structure/</id>
    <title>Directory structure</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-03T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/getting-started/directory-structure/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Directory structure</h1>
<h2 id="file-system-tree">File system tree</h2>
<p>Project files organization.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">&lt;mywebsite&gt;
├─ pages
|  ├─ blog            &lt;- Section
|  |  ├─ post-1.md    &lt;- Page in Section
|  |  └─ post-2.md
|  ├─ projects
|  |  └─ project-a.md
|  └─ about.md        &lt;- Root page
├─ assets
|  ├─ styles.scss     &lt;- Asset file
|  └─ logo.png
├─ static
|  └─ file.pdf        &lt;- Static file
└─ data
   └─ authors.yml     &lt;- Data collection</code></pre>
<h2 id="built-website-tree">Built website tree</h2>
<p>Result of the build.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">&lt;mywebsite&gt;
└─ _site
   ├─ index.html               &lt;- Generated home page
   ├─ blog/
   |  ├─ index.html            &lt;- Generated list of posts
   |  ├─ post-1/index.html     &lt;- A blog post
   |  └─ post-2/index.html
   ├─ projects/
   |  ├─ index.html
   |  └─ project-a/index.html
   ├─ about/index.html
   ├─ styles.css
   ├─ logo.png
   └─ file.pdf</code></pre>
<aside class="note note-info"><p>By default each page is generated as <code translate="no">slugified-filename/index.html</code> to get a “beautiful“ URL like <code translate="no">https://mywebsite.tld/section/slugified-filename/</code>.</p>
<p>To get an “ugly” URL (like <code translate="no">404.html</code> instead of <code translate="no">404/</code>), set <code translate="no">uglyurl: true</code> in page <a href="/content/pages/#front-matter">front matter</a>.</p></aside>
<h2 id="file-based-routing">File based routing</h2>
<p>Markdown files in the <code translate="no">pages</code> directory enable file based routing. Meaning that adding a <code translate="no">pages/my-projects/project-a.md</code> for instance will make it available at <code translate="no">/project-a</code> in your browser.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">File:
                   pages/my-projects/project-a.md
                        └───── filepath ──────┘
URL:
    ┌───── baseurl ─────┬─────── path ────────┐
     https://example.com/my-projects/project-a/index.html
                        └─ section ─┴─ slug ──┘</code></pre>
<aside class="note note-important"><p>Two kinds of prefixes can alter the URL. See the <a href="/content/pages/#file-prefix">File prefix section</a> below.</p></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/templates/reference/filters/</id>
    <title>Filters</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/templates/reference/filters/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Filters</h1>
<p>Variables can be modified by <a href="https://twig.symfony.com/doc/filters/index.html" target="_blank" rel="noopener noreferrer">filters</a>. Filters are separated from the variable by a pipe symbol (<code translate="no">|</code>). Multiple filters can be chained. The output of one filter is applied to the next.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ page.title|truncate(25)|<span class="hljs-keyword">capitalize</span> }}</span></code></pre>
<h2 id="filter-by">filter_by</h2>
<p>Filters a pages collection by variable name/value.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ collection|filter_by(variable, value) }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ pages|filter_by('section', 'blog') }}</span></code></pre>
<h2 id="filter">filter</h2>
<p>For more complex cases, you should use <a href="https://twig.symfony.com/doc/filters/filter.html" target="_blank" rel="noopener noreferrer">Twig’s native <code translate="no">filter</code></a>.</p>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name">pages</span>|<span class="hljs-keyword">filter</span>(p =&gt; p.virtual == false and p.id not in ['page-1', 'page-2']) %}</span></code></pre>
<h2 id="markdown-to-html">markdown_to_html</h2>
<p>Converts a Markdown string to HTML.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ markdown|markdown_to_html }}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">apply</span></span> markdown_to_html %}</span><span class="xml">
</span><span class="hljs-comment">{# Markdown here #}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endapply</span></span> %}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> markdown = '**This is bold text**' %}</span><span class="xml">
</span><span class="hljs-template-variable">{{ markdown|markdown_to_html }}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">apply</span></span> markdown_to_html %}</span><span class="xml">
**This is bold text**
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">endapply</span></span> %}</span></code></pre>
<h2 id="toc">toc</h2>
<p>Extracts only headings matching the given <code translate="no">selectors</code> (h2, h3, etc.), or those defined in config <code translate="no">pages.body.toc</code> if not specified.<br>
The <code translate="no">format</code> parameter defines the output format: <code translate="no">html</code> or <code translate="no">json</code>.<br>
The <code translate="no">url</code> parameter is used to build links to headings.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ markdown|toc(format, selectors, url) }}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ page.body|toc }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ page.body|toc('html') }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ page.body|toc(selectors=['h2']) }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ page.body|toc(url=url(page)) }}</span></code></pre>
<h2 id="json-decode">json_decode</h2>
<p>Converts a JSON string to an array.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ json|json_decode }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> json = '{"foo": "bar"}' %}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> array = json|json_decode %}</span><span class="xml">
</span><span class="hljs-template-variable">{{ array.foo }}</span></code></pre>
<h2 id="yaml-parse">yaml_parse</h2>
<p>Converts a YAML string to an array.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ yaml|yaml_parse }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> yaml = 'key: value' %}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> array = yaml|yaml_parse %}</span><span class="xml">
</span><span class="hljs-template-variable">{{ array.key }}</span></code></pre>
<h2 id="slugify">slugify</h2>
<p>Converts a string to a slug.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ string|slugify }}</span></code></pre>
<h2 id="u">u</h2>
<p>The <code translate="no">u</code> filter wraps a text in a Unicode object (a <a href="https://symfony.com/doc/current/components/string.html" target="_blank" rel="noopener noreferrer">Symfony UnicodeString instance</a>) that exposes methods to "manipulate" the string.</p>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ 'cecil_string with twig'|u.camel.title }}</span></code></pre>
<blockquote>
<p>CecilStringWithTwig</p>
</blockquote>
<h2 id="singular">singular</h2>
<p>The <code translate="no">singular</code> filter transforms a given noun in its plural form into its singular version.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ string|singular(locale)}}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-comment">{# English (en) rules are used by default #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ 'partitions'|singular }}</span></code></pre>
<blockquote>
<p>partition</p>
</blockquote>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ 'partitions'|singular('fr') }}</span></code></pre>
<blockquote>
<p>partition</p>
</blockquote>
<h2 id="plural">plural</h2>
<p>The <code translate="no">plural</code> filter transforms a given noun in its singular form into its plural version.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ string|plural(locale)}}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-comment">{# English (en) rules are used by default #}</span><span class="xml">
</span><span class="hljs-template-variable">{{ 'animal'|plural }}</span></code></pre>
<blockquote>
<p>animals</p>
</blockquote>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ 'animal'|plural('fr') }}</span></code></pre>
<blockquote>
<p>animaux</p>
</blockquote>
<h2 id="excerpt">excerpt</h2>
<p>Truncates a string and appends suffix.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ string|excerpt(length, suffix) }}</span></code></pre>
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody>
<tr>
<td>length</td>
<td>Truncates after this number of characters.</td>
<td>integer</td>
<td>450</td>
</tr>
<tr>
<td>suffix</td>
<td>Appends characters.</td>
<td>string</td>
<td><code translate="no">…</code></td>
</tr>
</tbody>
</table>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ variable|excerpt }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ variable|excerpt(250, '...') }}</span></code></pre>
<h2 id="excerpt-html">excerpt_html</h2>
<p>Reads characters before or after <code translate="no">&lt;!-- excerpt --&gt;</code> or <code translate="no">&lt;!-- break --&gt;</code> tag.<br>
See <a href="/content/markdown/#excerpt">Content documentation</a> for details.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ string|excerpt_html({separator, capture}) }}</span></code></pre>
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody>
<tr>
<td>separator</td>
<td>String to use as separator.</td>
<td>string</td>
<td><code translate="no">excerpt|break</code></td>
</tr>
<tr>
<td>capture</td>
<td>Part to capture, <code translate="no">before</code> or <code translate="no">after</code> the separator.</td>
<td>string</td>
<td><code translate="no">before</code></td>
</tr>
</tbody>
</table>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ variable|excerpt_html }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ variable|excerpt_html({separator: 'excerpt|break', capture: 'before'}) }}</span><span class="xml">
</span><span class="hljs-template-variable">{{ variable|excerpt_html({capture: 'after'}) }}</span></code></pre>
<h2 id="highlight">highlight</h2>
<p>Highlights a code string with <a href="https://github.com/scrivo/highlight.php" target="_blank" rel="noopener noreferrer">highlight.php</a>.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ code|highlight(language) }}</span></code></pre>
<p><em>Examples:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ '&lt;?php echo $highlighted-&gt;value; ?&gt;'|highlight('php') }}</span></code></pre>
<h2 id="preg-split">preg_split</h2>
<p>Splits a string into an array using a regular expression.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ string|preg_split(pattern, limit) }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> headers = page.content|preg_split('/&lt;br[^&gt;]*&gt;/') %}</span></code></pre>
<h2 id="preg-match-all">preg_match_all</h2>
<p>Performs a regular expression match and return the group for all matches.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ string|preg_match_all(pattern, group) }}</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name"><span class="hljs-keyword">set</span></span> tags = page.content|preg_match_all('/&lt;[^&gt;]+&gt;(.*)&lt;\/[^&gt;]+&gt;/') %}</span></code></pre>
<h2 id="hex-to-rgb">hex_to_rgb</h2>
<p>Converts a hexadecimal color to RGB.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ color|hex_to_rgb }}</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/pages/</id>
    <title>Pages</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/pages/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Pages</h1>
<h2 id="pages-dir">pages.dir</h2>
<p>Directory source of pages (<code translate="no">pages</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">dir:</span> <span class="hljs-string">pages</span></code></pre>
<h2 id="pages-ext">pages.ext</h2>
<p>Extensions of pages files.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">ext:</span> <span class="hljs-string">[md,</span> <span class="hljs-string">markdown,</span> <span class="hljs-string">mdown,</span> <span class="hljs-string">mkdn,</span> <span class="hljs-string">mkd,</span> <span class="hljs-string">text,</span> <span class="hljs-string">txt]</span></code></pre>
<h2 id="pages-exclude">pages.exclude</h2>
<p>Directories, paths and files name to exclude (accepts globs, strings and regexes).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">exclude:</span> <span class="hljs-string">['vendor',</span> <span class="hljs-string">'node_modules'</span><span class="hljs-string">,</span> <span class="hljs-string">'*.scss'</span><span class="hljs-string">,</span> <span class="hljs-string">'/\.bck$/'</span><span class="hljs-string">]</span></code></pre>
<h2 id="pages-prefix-separator">pages.prefix.separator</h2>
<p>List of characters used as separator between a filename prefix (<code translate="no">date</code> or <code translate="no">weight</code>) and the slug.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">prefix:</span>
    <span class="hljs-attr">separator:</span> <span class="hljs-string">['-',</span> <span class="hljs-string">'_'</span><span class="hljs-string">]</span></code></pre>
<h2 id="pages-sortby">pages.sortby</h2>
<p>Default collections sort method.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">sortby:</span> <span class="hljs-string">date</span> <span class="hljs-comment"># `date`, `updated`, `title` or `weight`</span>
  <span class="hljs-comment"># or</span>
  <span class="hljs-attr">sortby:</span>
    <span class="hljs-attr">variable:</span> <span class="hljs-string">date</span>    <span class="hljs-comment"># `date`, `updated`, `title` or `weight`</span>
    <span class="hljs-attr">desc_title:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># sort by title in descending order</span>
    <span class="hljs-attr">reverse:</span> <span class="hljs-literal">false</span>    <span class="hljs-comment"># reverse the sort order</span></code></pre>
<h2 id="pages-pagination">pages.pagination</h2>
<p>Pagination is available for list pages (<em>type</em> is <code translate="no">homepage</code>, <code translate="no">section</code> or <code translate="no">term</code>).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">pagination:</span>
    <span class="hljs-attr">max:</span> <span class="hljs-number">5</span>     <span class="hljs-comment"># maximum number of entries per page</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">page</span> <span class="hljs-comment"># path to the paginated page</span></code></pre>
<h3 id="disable-pagination">Disable pagination</h3>
<p>Pagination can be disabled:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">pagination:</span> <span class="hljs-literal">false</span></code></pre>
<h2 id="pages-paths">pages.paths</h2>
<p>Apply a custom <a href="/content/front-matter/#predefined-variables"><code translate="no">path</code></a> for all pages of a <strong><em>Section</em></strong>.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">paths:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">section:</span> <span class="hljs-string">&lt;section’s</span> <span class="hljs-string">ID&gt;</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">&lt;path</span> <span class="hljs-string">of</span> <span class="hljs-string">pages&gt;</span></code></pre>
<h3 id="path-placeholders">Path placeholders</h3>
<ul>
<li><code translate="no">:year</code></li>
<li><code translate="no">:month</code></li>
<li><code translate="no">:day</code></li>
<li><code translate="no">:section</code></li>
<li><code translate="no">:slug</code></li>
</ul>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">paths:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">section:</span> <span class="hljs-string">Blog</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">:section/:year/:month/:day/:slug</span> <span class="hljs-comment"># e.g.: /blog/2020/12/01/my-post/</span>
<span class="hljs-comment"># localized</span>
<span class="hljs-attr">languages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">code:</span> <span class="hljs-string">fr</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Français</span>
    <span class="hljs-attr">locale:</span> <span class="hljs-string">fr_FR</span>
    <span class="hljs-attr">config:</span>
      <span class="hljs-attr">pages:</span>
        <span class="hljs-attr">paths:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">section:</span> <span class="hljs-string">Blog</span>
            <span class="hljs-attr">path:</span> <span class="hljs-string">blogue/:year/:month/:day/:slug</span> <span class="hljs-comment"># e.g.: /blogue/2020/12/01/mon-billet/</span></code></pre>
<h2 id="pages-frontmatter">pages.frontmatter</h2>
<p>Page front matter format (<code translate="no">yaml</code> by default, also accepts <code translate="no">ini</code>, <code translate="no">toml</code> and <code translate="no">json</code>).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">frontmatter:</span> <span class="hljs-string">yaml</span></code></pre>
<h2 id="pages-body">pages.body</h2>
<p>Page body options.</p>
<aside class="note note-info"><p>To know how those options impacts your content see <em><a href="/content/markdown/">Content &gt; Markdown</a></em> documentation.</p></aside>
<h3 id="pages-body-toc">pages.body.toc</h3>
<p>Headers used to build the table of contents (<code translate="no">[h2, h3]</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">body:</span>
    <span class="hljs-attr">toc:</span> <span class="hljs-string">[h2,</span> <span class="hljs-string">h3]</span></code></pre>
<h3 id="pages-body-highlight">pages.body.highlight</h3>
<p>Enables code syntax highlighting (<code translate="no">true</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">body:</span>
    <span class="hljs-attr">highlight:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># set to false to disable syntax highlighting</span></code></pre>
<h3 id="pages-body-images">pages.body.images</h3>
<p>Images handling options.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">body:</span>
    <span class="hljs-attr">images:</span>
      <span class="hljs-attr">formats:</span> <span class="hljs-string">[]</span>       <span class="hljs-comment"># adds alternative image formats as `source` (e.g. `[avif, webp]`, empty array by default)</span>
      <span class="hljs-attr">resize:</span> <span class="hljs-number">0</span>         <span class="hljs-comment"># resizes all images to &lt;width&gt; (in pixels, `0` to disable)</span>
      <span class="hljs-attr">responsive:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># adds responsive image variants to the `srcset` attribute (`false` by default)</span>
      <span class="hljs-attr">lazy:</span> <span class="hljs-literal">true</span>        <span class="hljs-comment"># adds `loading="lazy"` attribute (`true` by default)</span>
      <span class="hljs-attr">decoding:</span> <span class="hljs-literal">true</span>    <span class="hljs-comment"># adds `decoding="async"` attribute (`true` by default)</span>
      <span class="hljs-attr">caption:</span> <span class="hljs-literal">false</span>    <span class="hljs-comment"># puts the image in a &lt;figure&gt; element and adds a &lt;figcaption&gt; containing the title (`false` by default)</span>
      <span class="hljs-attr">placeholder:</span> <span class="hljs-string">''</span>   <span class="hljs-comment"># fills the &lt;img&gt; background before loading ('color' or 'lqip', empty by default)</span>
      <span class="hljs-attr">class:</span> <span class="hljs-string">''</span>         <span class="hljs-comment"># sets a default class on each image (empty by default)</span>
      <span class="hljs-attr">dark_suffix:</span> <span class="hljs-string">''</span>   <span class="hljs-comment"># suffix of the dark variant image (e.g. `.dark`), disabled by default</span>
      <span class="hljs-attr">mobile_suffix:</span> <span class="hljs-string">''</span> <span class="hljs-comment"># suffix of the mobile variant image (e.g. `.mobile`), disabled by default</span>
      <span class="hljs-attr">mobile_media_query:</span> <span class="hljs-string">'(max-width: 767px)'</span> <span class="hljs-comment"># media query of the mobile variant `&lt;source&gt;`</span>
      <span class="hljs-attr">remote:</span>           <span class="hljs-comment"># remote image handling (set to `false` to disable)</span>
        <span class="hljs-attr">fallback:</span>         <span class="hljs-comment"># path to the fallback image, stored in assets dir (empty by default)</span></code></pre>
<aside class="note note-warning"><p>Since version <ins>8.41.0</ins>, the <code translate="no">pages.body.images.resize</code> option is used to resize images to a specific width, no more to enable the resize feature (enabled systematically).</p></aside>
<aside class="note note-important"><p>Global options, like responsives images widths and sizes, are configurable in the <a href="/documentation/assets/#assets-images"><code translate="no">assets.images</code></a> section.</p></aside>
<aside class="note note-info"><p>Remote images are downloaded and converted into <em>Assets</em> to be manipulated. You can disable this behavior by setting the option <code translate="no">pages.body.images.remote.enabled</code> to <code translate="no">false</code>.</p></aside>
<aside class="note note-tip"><p>When <code translate="no">dark_suffix</code> is set (e.g. <code translate="no">dark_suffix: .dark</code>), Cecil automatically looks for a dark variant of each image (e.g. <code translate="no">photo.dark.jpg</code> alongside <code translate="no">photo.jpg</code>). If found, the image is wrapped in a <code translate="no">&lt;picture&gt;</code> element with a <code translate="no">&lt;source media="(prefers-color-scheme: dark)"&gt;</code> for automatic light/dark theme switching. Works in combination with <code translate="no">formats</code> and <code translate="no">responsive</code>.</p>
<p>In the same way, when <code translate="no">mobile_suffix</code> is set (e.g. <code translate="no">mobile_suffix: .mobile</code>), Cecil looks for a mobile variant of each image (e.g. <code translate="no">photo.mobile.jpg</code> alongside <code translate="no">photo.jpg</code>) and adds a <code translate="no">&lt;source&gt;</code> element with the <code translate="no">mobile_media_query</code> media query (<code translate="no">(max-width: 767px)</code> by default). If <code translate="no">dark_suffix</code> is also set, the dark variant of the mobile image (e.g. <code translate="no">photo.mobile.dark.jpg</code>) is used on mobile with dark color scheme. Mobile sources are placed before dark sources, so that a mobile variant takes precedence.</p></aside>
<h3 id="pages-body-links">pages.body.links</h3>
<p>Links handling options.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">body:</span>
    <span class="hljs-attr">links:</span>
      <span class="hljs-attr">embed:</span>
        <span class="hljs-attr">enabled:</span> <span class="hljs-literal">false</span>     <span class="hljs-comment"># turns links in embedded content if possible (`false` by default)</span>
        <span class="hljs-attr">video:</span> <span class="hljs-string">[mp4,</span> <span class="hljs-string">webm]</span> <span class="hljs-comment"># video files extensions</span>
        <span class="hljs-attr">audio:</span> <span class="hljs-string">[mp3]</span>       <span class="hljs-comment"># audio files extensions</span>
      <span class="hljs-attr">external:</span>
        <span class="hljs-attr">blank:</span> <span class="hljs-literal">false</span>     <span class="hljs-comment"># if true open external link in new tab</span>
        <span class="hljs-attr">noopener:</span> <span class="hljs-literal">true</span>   <span class="hljs-comment"># if true add "noopener" to `rel` attribute</span>
        <span class="hljs-attr">noreferrer:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># if true add "noreferrer" to `rel` attribute</span>
        <span class="hljs-attr">nofollow:</span> <span class="hljs-literal">false</span>  <span class="hljs-comment"># if true add "nofollow" to `rel` attribute</span></code></pre>
<h3 id="pages-body-excerpt">pages.body.excerpt</h3>
<p>Excerpt handling options.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">body:</span>
    <span class="hljs-attr">excerpt:</span>
      <span class="hljs-attr">separator:</span> <span class="hljs-string">excerpt|break</span> <span class="hljs-comment"># string to use as separator (`excerpt|break` by default)</span>
      <span class="hljs-attr">capture:</span> <span class="hljs-string">before</span>          <span class="hljs-comment"># part to capture, `before` or `after` the separator (`before` by default)</span></code></pre>
<h2 id="pages-virtual">pages.virtual</h2>
<p>Virtual pages is the best way to create pages without content (<strong>front matter only</strong>).</p>
<p>It consists of a list of pages with a <code translate="no">path</code> and some front matter variables.</p>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">virtual:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">code</span>
      <span class="hljs-attr">redirect:</span> <span class="hljs-string">https://github.com/ArnaudLigny</span></code></pre>
<h2 id="pages-default">pages.default</h2>
<p>Default pages are pages created automatically by Cecil (from built-in templates):</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">default:</span>
    <span class="hljs-attr">index:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">''</span>
      <span class="hljs-attr">title:</span> <span class="hljs-string">Home</span>
      <span class="hljs-attr">published:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">404:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-number">404</span>
      <span class="hljs-attr">title:</span> <span class="hljs-string">Page</span> <span class="hljs-string">not</span> <span class="hljs-string">found</span>
      <span class="hljs-attr">layout:</span> <span class="hljs-number">404</span>
      <span class="hljs-attr">uglyurl:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">published:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">excluded:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">robots:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">robots</span>
      <span class="hljs-attr">title:</span> <span class="hljs-string">Robots.txt</span>
      <span class="hljs-attr">layout:</span> <span class="hljs-string">robots</span>
      <span class="hljs-attr">output:</span> <span class="hljs-string">txt</span>
      <span class="hljs-attr">published:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">excluded:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">multilingual:</span> <span class="hljs-literal">false</span>
    <span class="hljs-attr">sitemap:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">sitemap</span>
      <span class="hljs-attr">title:</span> <span class="hljs-string">XML</span> <span class="hljs-string">sitemap</span>
      <span class="hljs-attr">layout:</span> <span class="hljs-string">sitemap</span>
      <span class="hljs-attr">output:</span> <span class="hljs-string">xml</span>
      <span class="hljs-attr">changefreq:</span> <span class="hljs-string">monthly</span>
      <span class="hljs-attr">priority:</span> <span class="hljs-number">0.5</span>
      <span class="hljs-attr">published:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">excluded:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">multilingual:</span> <span class="hljs-literal">false</span>
    <span class="hljs-attr">xsl/atom:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">xsl/atom</span>
      <span class="hljs-attr">layout:</span> <span class="hljs-string">feed</span>
      <span class="hljs-attr">output:</span> <span class="hljs-string">xsl</span>
      <span class="hljs-attr">uglyurl:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">published:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">excluded:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">xsl/rss:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">xsl/rss</span>
      <span class="hljs-attr">layout:</span> <span class="hljs-string">feed</span>
      <span class="hljs-attr">output:</span> <span class="hljs-string">xsl</span>
      <span class="hljs-attr">uglyurl:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">published:</span> <span class="hljs-literal">false</span>
      <span class="hljs-attr">excluded:</span> <span class="hljs-literal">true</span></code></pre>
<aside class="note note-info"><p>The structure is almost identical of <a href="#pages-virtual"><code translate="no">pages.virtual</code></a>, except the named key.</p></aside>
<p>Each one can be:</p>
<ol>
<li>disabled: <code translate="no">published: false</code></li>
<li>excluded from list pages: <code translate="no">excluded: true</code></li>
<li>excluded from localization: <code translate="no">multilingual: false</code></li>
</ol>
<aside class="note note-tip"><p>Since version 8.68.0 you can override the default <code translate="no">robots.txt</code> page by creating a page with the same <code translate="no">path</code>:</p>
<p><em>pages/robots.md</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">layout:</span> <span class="hljs-string">robots</span>
<span class="hljs-attr">output:</span> <span class="hljs-string">txt</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">User-agent:</span> <span class="hljs-string">AI-bot</span>
<span class="hljs-attr">Disallow:</span> <span class="hljs-string">/</span></code></pre></aside>
<h2 id="pages-generators">pages.generators</h2>
<p>Generators are used by Cecil to create additional pages (e.g.: sitemap, feed, pagination, etc.) from existing pages, or from other sources like the configuration file or external sources.</p>
<p>Below the list of Generators provided by Cecil, in a defined order:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">generators:</span>
    <span class="hljs-attr">10:</span> <span class="hljs-string">'Cecil\Generator\DefaultPages'</span>
    <span class="hljs-attr">20:</span> <span class="hljs-string">'Cecil\Generator\VirtualPages'</span>
    <span class="hljs-attr">30:</span> <span class="hljs-string">'Cecil\Generator\ExternalBody'</span>
    <span class="hljs-attr">40:</span> <span class="hljs-string">'Cecil\Generator\Section'</span>
    <span class="hljs-attr">50:</span> <span class="hljs-string">'Cecil\Generator\Taxonomy'</span>
    <span class="hljs-attr">60:</span> <span class="hljs-string">'Cecil\Generator\Homepage'</span>
    <span class="hljs-attr">70:</span> <span class="hljs-string">'Cecil\Generator\Pagination'</span>
    <span class="hljs-attr">80:</span> <span class="hljs-string">'Cecil\Generator\Alias'</span>
    <span class="hljs-attr">90:</span> <span class="hljs-string">'Cecil\Generator\Redirect'</span></code></pre>
<aside class="note note-tip"><p>You can extend Cecil with <a href="/developers/extend/#pages-generator">Pages generator</a>.</p></aside>
<h2 id="pages-subsets">pages.subsets</h2>
<p>Subsets are used to render a part of the pages collection, based on a specific path, language or output format, with the command:</p>
<pre><code class="language-bash hljs bash" translate="no">cecil build --render-subset=&lt;name&gt;</code></pre>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">subsets:</span>
    <span class="hljs-string">&lt;name&gt;:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">&lt;path&gt;</span> <span class="hljs-comment"># glob or string path (e.g.: `blog/*`, `blog`)</span>
      <span class="hljs-attr">language:</span> <span class="hljs-string">&lt;language&gt;</span> <span class="hljs-comment"># language code (e.g.: `en`, `fr`)</span>
      <span class="hljs-attr">output:</span> <span class="hljs-string">&lt;output&gt;</span> <span class="hljs-comment"># output format (e.g.: `html`, `atom`)</span></code></pre>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">pages:</span>
  <span class="hljs-attr">subsets:</span>
    <span class="hljs-attr">blog_en:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">blog</span>
      <span class="hljs-attr">language:</span> <span class="hljs-string">en</span>
      <span class="hljs-attr">output:</span> <span class="hljs-string">html</span>
    <span class="hljs-attr">search_index:</span>
      <span class="hljs-attr">path:</span> <span class="hljs-string">'*'</span>
      <span class="hljs-attr">output:</span> <span class="hljs-string">json</span></code></pre>
<hr>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/content/multilingual/</id>
    <title>Multilingual</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-03T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/content/multilingual/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Multilingual</h1>
<p>If your pages are available in multiple <a href="/configuration/languages/#languages">languages</a> there is 2 different ways to define it:</p>
<h2 id="through-file-name">Through file name</h2>
<p>This is the common way to translate a page from the main <a href="/configuration/languages/#language">language</a> to another language.</p>
<p>You just need to duplicate the reference page and suffix it with the target language <code translate="no">code</code> (e.g.: <code translate="no">fr</code>).</p>
<p><em>Example:</em></p>
<pre><code class="language-plaintext hljs plaintext" translate="no">├─ about.md    # the reference page
└─ about.fr.md # the french version (`fr`)</code></pre>
<aside class="note note-tip"><p>You can change the URL of the translated page with the <code translate="no">slug</code> variable in the front matter. For example:</p>
<pre><code class="language-yml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">slug:</span> <span class="hljs-string">a-propos</span>
<span class="hljs-meta">---</span>
<span class="hljs-comment"># about.md    -&gt; /about/</span>
<span class="hljs-comment"># about.fr.md -&gt; /fr/a-propos/</span></code></pre></aside>
<h2 id="through-front-matter">Through front matter</h2>
<p>If you want to create a page in a language other than the main language, without it being a translation of an existing page, you can use the <code translate="no">language</code> variable in its front matter.</p>
<p><em>Example:</em></p>
<pre><code class="language-yml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">language:</span> <span class="hljs-string">fr</span>
<span class="hljs-meta">---</span></code></pre>
<h2 id="link-translated-pages">Link translated pages</h2>
<p>Each translated page reference the pages in others languages.</p>
<p>Those pages collection is available in <a href="/templates/variables/#page">templates</a> with the following variable:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ page.translations }}</span></code></pre>
<aside class="note note-info"><p>The <code translate="no">langref</code> variable is provided by default, but you can change it in the front matter:</p>
<pre><code class="language-yml hljs yaml" translate="no"><span class="hljs-meta">---</span>
<span class="hljs-attr">langref:</span> <span class="hljs-string">my-page-ref</span>
<span class="hljs-meta">---</span></code></pre></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/templates/components/</id>
    <title>Components</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/templates/components/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Components</h1>
<p>Cecil provides a components logic to give you the power making reusable template "units".</p>
<aside class="note note-info"><p>The components feature is provided by the <a href="https://github.com/giorgiopogliani/twig-components" target="_blank" rel="noopener noreferrer"><em>Twig components extension</em></a> created by Giorgio Pogliani.</p></aside>
<h2 id="components-syntax">Components syntax</h2>
<p>Components are just Twig templates stored in the <code translate="no">components/</code> subdirectory and can be used anywhere in your templates:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-comment">{# /components/button.twig #}</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> </span></span><span class="hljs-template-variable">{{ attributes.merge({class: 'rounded px-4'}) }}</span><span class="xml"><span class="hljs-tag">&gt;</span>
    </span><span class="hljs-template-variable">{{ slot }}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span></code></pre>
<blockquote>
<p>The slot variable is any content you will add between the opening and the close tag.</p>
</blockquote>
<p>To reach a component you need to use the dedicated tag <code translate="no">x</code> followed by <code translate="no">:</code> and the filename of your component without extension:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-comment">{# /index.twig #}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name">x</span>:button with {class: 'text-white'} %}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Click me<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>
</span><span class="hljs-template-tag">{% <span class="hljs-name">endx</span> %}</span></code></pre>
<p>It will render:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-white rounded px-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Click me<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/data-static/</id>
    <title>Data and static files</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/data-static/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Data and static files</h1>
<h2 id="data">Data</h2>
<p>Where data files are stored and what extensions are handled.</p>
<p>Supported formats: YAML, JSON, XML and CSV.</p>
<h3 id="data-dir">data.dir</h3>
<p>Data source directory (<code translate="no">data</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">data:</span>
  <span class="hljs-attr">dir:</span> <span class="hljs-string">data</span></code></pre>
<h3 id="data-ext">data.ext</h3>
<p>Array of files extensions.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">data:</span>
  <span class="hljs-attr">ext:</span> <span class="hljs-string">[yaml,</span> <span class="hljs-string">yml,</span> <span class="hljs-string">json,</span> <span class="hljs-string">xml,</span> <span class="hljs-string">csv]</span></code></pre>
<h3 id="data-load">data.load</h3>
<p>Enables <code translate="no">site.data</code> collection (<code translate="no">true</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">data:</span>
  <span class="hljs-attr">load:</span> <span class="hljs-literal">true</span></code></pre>
<hr>
<h2 id="static">Static</h2>
<p>Management of static files are copied (PDF, fonts, etc.).</p>
<aside class="note note-important"><p>You should put your assets files, used by <a href="/assets/#asset"><code translate="no">asset()</code></a>, in the <a href="/documentation/assets/#assets-dir"><code translate="no">assets</code> directory</a> to avoid unnecessary files copy.</p></aside>
<h3 id="static-dir">static.dir</h3>
<p>Static files source directory (<code translate="no">static</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">static:</span>
  <span class="hljs-attr">dir:</span> <span class="hljs-string">static</span></code></pre>
<h3 id="static-target">static.target</h3>
<p>Directory where static files are copied (root by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">static:</span>
  <span class="hljs-attr">target:</span> <span class="hljs-string">''</span></code></pre>
<h3 id="static-exclude">static.exclude</h3>
<p>List of excluded files. Accepts globs, strings and regexes.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">static:</span>
  <span class="hljs-attr">exclude:</span> <span class="hljs-string">['sass',</span> <span class="hljs-string">'scss'</span><span class="hljs-string">,</span> <span class="hljs-string">'*.scss'</span><span class="hljs-string">,</span> <span class="hljs-string">'package*.json'</span><span class="hljs-string">,</span> <span class="hljs-string">'node_modules'</span><span class="hljs-string">]</span></code></pre>
<aside class="note note-tip"><p>If you use <a href="https://icons.getbootstrap.com" target="_blank" rel="noopener noreferrer">Bootstrap Icons</a> you can exclude the <code translate="no">node_modules</code> except <code translate="no">node_modules/bootstrap-icons</code> with a regular expression:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">exclude:</span> <span class="hljs-string">['sass',</span> <span class="hljs-string">'scss'</span><span class="hljs-string">,</span> <span class="hljs-string">'*.scss'</span><span class="hljs-string">,</span> <span class="hljs-string">'package*.json'</span><span class="hljs-string">,</span> <span class="hljs-string">'#node_modules/(?!bootstrap-icons)#'</span><span class="hljs-string">]</span></code></pre></aside>
<h3 id="static-load">static.load</h3>
<p>Enables <code translate="no">site.static</code> collection (<code translate="no">false</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">static:</span>
  <span class="hljs-attr">load:</span> <span class="hljs-literal">false</span></code></pre>
<h3 id="static-mounts">static.mounts</h3>
<p>Allows to copy specific files or directories to a specific destination.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">static:</span>
  <span class="hljs-attr">mounts:</span> <span class="hljs-string">[]</span></code></pre>
<h3 id="static-example">static example</h3>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">static:</span>
  <span class="hljs-attr">dir:</span> <span class="hljs-string">docs</span>
  <span class="hljs-attr">target:</span> <span class="hljs-string">docs</span>
  <span class="hljs-attr">exclude:</span> <span class="hljs-string">['sass',</span> <span class="hljs-string">'*.scss'</span><span class="hljs-string">,</span> <span class="hljs-string">'/\.bck$/'</span><span class="hljs-string">]</span>
  <span class="hljs-attr">load:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">mounts:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">source/path/file.ext:</span> <span class="hljs-string">dest/path/file.ext</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">node_modules/bootstrap-icons/font/fonts:</span> <span class="hljs-string">fonts</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/content/dynamic-content/</id>
    <title>Dynamic content</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-03T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/content/dynamic-content/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Dynamic content</h1>
<p>You can create dynamic content in a page by using the <a href="https://twig.symfony.com/doc/3.x/functions/template_from_string.html" target="_blank" rel="noopener noreferrer"><code translate="no">template_from_string</code></a> Twig function.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ <span class="hljs-name">include</span><span class="hljs-params">(template_from_string(page.content, "dynamic content for page " ~ page.id)</span>) }}</span></code></pre>
<p>With this, you can use any page variable in the <em>body</em> of the page.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="xml">--
var: 'value'
---
The value of `var` is </span><span class="hljs-template-variable">{{ page.var }}</span><span class="xml">.</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/templates/localization/</id>
    <title>Localization</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/templates/localization/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Localization</h1>
<p>Cecil support <a href="#text-translation">text translation</a> and <a href="#date-localization">date localization</a>.</p>
<h2 id="text-translation">Text translation</h2>
<p>Uses the <code translate="no">trans</code> <em>tag</em> or <em>filter</em> to translate texts in templates.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name">trans</span> with variables into locale %}</span><span class="hljs-template-tag">{% <span class="hljs-name">endtrans</span> %}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ message|trans(variables = []) }}</span></code></pre>
<h3 id="examples">Examples</h3>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name">trans</span> %}</span><span class="xml">Hello World!</span><span class="hljs-template-tag">{% <span class="hljs-name">endtrans</span> %}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ message|trans }}</span></code></pre>
<p>Include variables:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name">trans</span> with {'%name%': 'Arnaud'} %}</span><span class="xml">Hello %name%!</span><span class="hljs-template-tag">{% <span class="hljs-name">endtrans</span> %}</span></code></pre>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ message|trans({'%name%': 'Arnaud'}) }}</span></code></pre>
<p>Force locale:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name">trans</span> into 'fr_FR' %}</span><span class="xml">Hello World!</span><span class="hljs-template-tag">{% <span class="hljs-name">endtrans</span> %}</span></code></pre>
<p>Pluralize:</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name">trans</span> with {'%count%': 42}%}</span><span class="xml">{0}I don't have apples|{1}I have one apple|]1,Inf[I have %count% apples</span><span class="hljs-template-tag">{% <span class="hljs-name">endtrans</span> %}</span></code></pre>
<h2 id="translation-files">Translation files</h2>
<p>Translation files must be named <code translate="no">messages.&lt;locale&gt;.&lt;extension&gt;</code> and stored in the <a href="/configuration/layouts/"><code translate="no">translations</code></a> directory.<br>
Supported file extensions are defined by each translation format in <a href="/configuration/layouts/#layouts-translations"><code translate="no">layouts.translations.formats</code></a>.</p>
<p>The locale code (e.g.: <code translate="no">fr_FR</code>) of a language is defined in the <a href="/configuration/languages/#languages"><code translate="no">languages</code></a> entries of the configuration.</p>
<p><em>Example:</em></p>
<pre><code class="language-plaintext hljs plaintext" translate="no">&lt;mywebsite&gt;
└─ translations
   ├─ messages.fr_FR.mo   &lt;- Machine Object format
   └─ messages.fr_FR.yaml &lt;- Yaml format</code></pre>
<aside class="note note-info"><p>You can easily extract translations from your templates with the following command:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar util:translations:extract --locale=&lt;code&gt; --show</code></pre>
<p>Use <code translate="no">--save</code> instead of (or in addition to) <code translate="no">--show</code> to save the translations to a file. The <code translate="no">--locale</code> option is required. The default output format is <code translate="no">yaml</code> (use <code translate="no">--format=po</code> for gettext PO format).</p></aside>
<aside class="note note-tip"><p><a href="https://poedit.net" target="_blank" rel="noopener noreferrer"><em>Poedit</em></a> is a simple and cross platform translation editor for gettext (PO), and <a href="https://poedit.net/pro" target="_blank" rel="noopener noreferrer"><em>Poedit Pro</em></a> supports extraction of translation strings from templates out of the box.</p></aside>
<aside class="note note-important"><p>Be careful about the <a href="/documentation/cache/">cache</a> when you update translations files.</p>
<p>Cache can be cleared with with the following command:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar cache:clear:translations`</code></pre></aside>
<h2 id="date-localization">Date localization</h2>
<p>Uses the Twig <a href="https://twig.symfony.com/doc/3.x/filters/format_date.html" target="_blank" rel="noopener noreferrer"><code translate="no">format_date</code></a> filter to localize a date in templates.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-variable">{{ page.<span class="hljs-name">date</span>|format_date('long') }}</span><span class="xml">
</span><span class="hljs-comment">{# September 30, 2022 #}</span></code></pre>
<p>Supported values are: <code translate="no">short</code>, <code translate="no">medium</code>, <code translate="no">long</code>, and <code translate="no">full</code>.</p>
<aside class="note note-important"><p>If you want to use the <code translate="no">format_date</code> filter <strong>with other locales than "en"</strong>, you should <a href="https://php.net/intl.setup" target="_blank" rel="noopener noreferrer">install the intl PHP extension</a>.</p></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/assets/</id>
    <title>Assets</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/assets/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Assets</h1>
<p>Assets management (images, CSS and JS files).</p>
<h2 id="assets-dir">assets.dir</h2>
<p>Assets source directory (<code translate="no">assets</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">dir:</span> <span class="hljs-string">assets</span></code></pre>
<h2 id="assets-target">assets.target</h2>
<p>Directory where remote and resized assets files are saved (root by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">target:</span> <span class="hljs-string">''</span></code></pre>
<h2 id="assets-fingerprint">assets.fingerprint</h2>
<p>Enables fingerprinting (cache busting) for assets files (<code translate="no">true</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">fingerprint:</span> <span class="hljs-literal">true</span></code></pre>
<h2 id="assets-compile">assets.compile</h2>
<p>Enables <a href="https://sass-lang.com" target="_blank" rel="noopener noreferrer">Sass</a> files compilation (<code translate="no">true</code> by default). See the <a href="https://scssphp.github.io/scssphp/docs/#output-formatting" target="_blank" rel="noopener noreferrer">documentation of scssphp</a> for options details.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">compile:</span>
    <span class="hljs-attr">style:</span> <span class="hljs-string">expanded</span>      <span class="hljs-comment"># compilation style (`expanded` or `compressed`. `expanded` by default)</span>
    <span class="hljs-attr">import:</span> <span class="hljs-string">[sass,</span> <span class="hljs-string">scss]</span> <span class="hljs-comment"># list of imported paths (`[sass, scss, node_modules]` by default)</span>
    <span class="hljs-attr">sourcemap:</span> <span class="hljs-literal">false</span>     <span class="hljs-comment"># enables sourcemap in debug mode (`false` by default)</span>
    <span class="hljs-attr">variables:</span> <span class="hljs-string">[]</span>        <span class="hljs-comment"># list of preset variables (empty by default)</span></code></pre>
<aside class="note note-info"><p><code translate="no">sourcemap</code> is used to debug SCSS compilation (<a href="/documentation/site/#debug">debug mode</a> must be enabled).</p></aside>
<h2 id="assets-minify">assets.minify</h2>
<p>Enables CSS and JS minification (<code translate="no">true</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">minify:</span> <span class="hljs-literal">true</span></code></pre>
<h2 id="assets-images">assets.images</h2>
<p>Images management.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">optimize:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># enables images optimization with JpegOptim, Optipng, Pngquant 2, SVGO 1, Gifsicle, cwebp, avifenc (`false` by default)</span>
    <span class="hljs-attr">quality:</span> <span class="hljs-number">75</span>     <span class="hljs-comment"># image quality of `optimize` and `resize` (`75` by default)</span>
    <span class="hljs-attr">responsive:</span>
      <span class="hljs-attr">widths:</span> <span class="hljs-string">[480,</span> <span class="hljs-number">640</span><span class="hljs-string">,</span> <span class="hljs-number">768</span><span class="hljs-string">,</span> <span class="hljs-number">1024</span><span class="hljs-string">,</span> <span class="hljs-number">1366</span><span class="hljs-string">,</span> <span class="hljs-number">1600</span><span class="hljs-string">,</span> <span class="hljs-number">1920</span><span class="hljs-string">]</span> <span class="hljs-comment"># `srcset` attribute images widths</span>
      <span class="hljs-attr">sizes:</span>
        <span class="hljs-attr">default:</span> <span class="hljs-string">'100vw'</span> <span class="hljs-comment"># default `sizes` attribute (`100vw` by default)</span></code></pre>
<h2 id="assets-images-cdn">assets.images.cdn</h2>
<p>URL of image assets can be easily replaced by a provided CDN <code translate="no">url</code>.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">cdn:</span>
      <span class="hljs-attr">enabled:</span> <span class="hljs-literal">false</span>  <span class="hljs-comment"># enables Image CDN (`false` by default)</span>
      <span class="hljs-attr">canonical:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># `image_url` is canonical (instead of a relative path) (`true` by default)</span>
      <span class="hljs-attr">remote:</span> <span class="hljs-literal">true</span>    <span class="hljs-comment"># handles not local images too (`true` by default)</span>
      <span class="hljs-attr">account:</span> <span class="hljs-string">'xxxx'</span> <span class="hljs-comment"># provider account</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">'https://provider.tld/%account%/%image_url%?w=%width%&amp;q=%quality%&amp;format=%format%'</span></code></pre>
<p><code translate="no">url</code> is a pattern that contains variables:</p>
<ul>
<li><code translate="no">%account%</code> replaced by the <code translate="no">assets.images.cdn.account</code> option</li>
<li><code translate="no">%image_url%</code> replaced by the image canonical URL or <code translate="no">path</code></li>
<li><code translate="no">%width%</code> replaced by the image width</li>
<li><code translate="no">%quality%</code> replaced by the <code translate="no">assets.images.quality</code> option</li>
<li><code translate="no">%format%</code> replaced by the image format</li>
</ul>
<p>See <a href="/assets/cdn-providers/"><strong>CDN providers</strong></a>.</p>
<h2 id="assets-remote-useragent">assets.remote.useragent</h2>
<p>User agent used to download remote assets.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">assets:</span>
  <span class="hljs-attr">remote:</span>
    <span class="hljs-attr">useragent:</span>
      <span class="hljs-attr">default:</span> <span class="hljs-string">&lt;string&gt;</span> <span class="hljs-comment"># default user agent</span>
      <span class="hljs-attr">useragent1:</span> <span class="hljs-string">&lt;string&gt;</span>
      <span class="hljs-attr">useragent2:</span> <span class="hljs-string">&lt;string&gt;</span></code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/templates/cache/</id>
    <title>Cache</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/templates/cache/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Cache</h1>
<p>Cecil uses a cache system to speed up the generation process, it can be disabled or cleared.</p>
<p>There are three cache types involved in template rendering: templates, <a href="/assets/#asset">assets</a>, and <a href="/documentation/localization/#translation-files">translations</a>.</p>
<h2 id="clear-cache">Clear cache</h2>
<p>You can clear the cache with the following commands:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar cache:clear               <span class="hljs-comment"># clear all caches</span>
php cecil.phar cache:clear:assets        <span class="hljs-comment"># clear assets cache</span>
php cecil.phar cache:clear:templates     <span class="hljs-comment"># clear templates cache</span>
php cecil.phar cache:clear:translations  <span class="hljs-comment"># clear translations cache</span></code></pre>
<aside class="note note-important"><p>In practice you don't need to clear the cache manually, Cecil does it for you when needed (e.g. when files change).</p></aside>
<h2 id="fragments-cache">Fragments cache</h2>
<p>Cecil provides a way to cache parts of templates rendering to avoid re-rendering the same partial content multiple times.</p>
<p>To use <em>fragments</em> cache, you must wrap the content you want to cache with the <a href="https://twig.symfony.com/doc/tags/cache.html" target="_blank" rel="noopener noreferrer"><code translate="no">cache</code> tag</a>.</p>
<pre><code class="language-twig hljs twig" translate="no"><span class="hljs-template-tag">{% <span class="hljs-name">cache</span> 'unique-key' %}</span><span class="xml">
  </span><span class="hljs-comment">{# cacheable content #}</span><span class="xml">
</span><span class="hljs-template-tag">{% <span class="hljs-name">endcache</span> %}</span></code></pre>
<aside class="note note-tip"><p>You should use the <a href="/documentation/reference/functions/#cache-key"><code translate="no">cache_key</code> function</a> to be sure to have a unique cache key for each content you want to cache.</p></aside>
<aside class="note note-warning"><p><em>Fragments</em> cache is persistent, so if the cache key is too generic, you may end up with wrong content displayed.</p></aside>
<p>To clear fragments cache only, you can use the following command:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar cache:clear:templates --fragments</code></pre>
<h2 id="disable-cache">Disable cache</h2>
<p>You can disable cache with the <a href="/configuration/cache/">configuration</a>.</p>
<aside class="note note-warning"><p>Disabling cache can slow down the generation process, so it's not recommended.</p>
<p>During local development, if you need to clear cache before each generation, you can use the following option:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar serve --clear-cache          <span class="hljs-comment"># clear all caches</span>
php cecil.phar serve --clear-cache=&lt;regex&gt;  <span class="hljs-comment"># clear cache for cache key matches with the regular expression &lt;regex&gt;</span></code></pre>
<p>Example:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar serve --clear-cache=css  <span class="hljs-comment"># clear cache for all CSS files</span></code></pre></aside>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/layouts/</id>
    <title>Layouts</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/layouts/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Layouts</h1>
<p>Templates options.</p>
<h2 id="layouts-dir">layouts.dir</h2>
<p>Templates directory source (<code translate="no">layouts</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">layouts:</span>
  <span class="hljs-attr">dir:</span> <span class="hljs-string">layouts</span></code></pre>
<h2 id="layouts-autoescape">layouts.autoescape</h2>
<p>Overrides Twig <code translate="no">autoescape</code> option (<code translate="no">false</code> by default).</p>
<p>If set to <code translate="no">null</code>, Cecil uses an extension-based strategy:</p>
<ul>
<li><code translate="no">*.js.twig</code> -&gt; <code translate="no">js</code></li>
<li><code translate="no">*.css.twig</code> -&gt; <code translate="no">css</code></li>
<li><code translate="no">*.html.twig</code> and <code translate="no">*.twig</code> -&gt; <code translate="no">html</code></li>
<li>any other extension -&gt; <code translate="no">false</code></li>
</ul>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">layouts:</span>
  <span class="hljs-attr">autoescape:</span> <span class="hljs-literal">false</span>  <span class="hljs-comment"># disables automatic escaping (default) </span>
  <span class="hljs-comment">#autoescape: null  # use Cecil automatic strategy by template filename extension </span>
  <span class="hljs-comment">#autoescape: html</span>
  <span class="hljs-comment">#autoescape: js</span></code></pre>
<h2 id="layouts-images">layouts.images</h2>
<p>Images handling options.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">layouts:</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">formats:</span> <span class="hljs-string">[]</span>       <span class="hljs-comment"># used by `html` function: adds alternatives image formats as `source` (e.g. `[avif, webp]`, empty array by default)</span>
    <span class="hljs-attr">responsive:</span> <span class="hljs-literal">false</span> <span class="hljs-comment"># used by `html` function: adds responsive images ('width' or 'density', `false` by default)</span>
    <span class="hljs-attr">placeholder:</span> <span class="hljs-string">''</span>   <span class="hljs-comment"># used by `html` function: fills image background before loading (`color` or `lqip`, disabled by default)</span>
    <span class="hljs-attr">dark_suffix:</span> <span class="hljs-string">''</span>   <span class="hljs-comment"># suffix of the dark variant image (e.g. `.dark`), disabled by default</span>
    <span class="hljs-attr">mobile_suffix:</span> <span class="hljs-string">''</span> <span class="hljs-comment"># suffix of the mobile variant image (e.g. `.mobile`), disabled by default</span>
    <span class="hljs-attr">mobile_media_query:</span> <span class="hljs-string">'(max-width: 767px)'</span> <span class="hljs-comment"># media query of the mobile variant `&lt;source&gt;`</span></code></pre>
<h2 id="layouts-translations">layouts.translations</h2>
<p>Translations handling options.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">layouts:</span>
  <span class="hljs-attr">translations:</span>
    <span class="hljs-attr">dir:</span> <span class="hljs-string">translations</span> <span class="hljs-comment"># translations source directory (`translations` by default)</span>
    <span class="hljs-attr">formats:</span>          <span class="hljs-comment"># translations supported formats</span>
      <span class="hljs-attr">yaml:</span>
        <span class="hljs-attr">loader:</span> <span class="hljs-string">Symfony\Component\Translation\Loader\YamlFileLoader</span>
        <span class="hljs-attr">ext:</span> <span class="hljs-string">[yml,</span> <span class="hljs-string">yaml]</span>
      <span class="hljs-attr">mo:</span>
        <span class="hljs-attr">loader:</span> <span class="hljs-string">Symfony\Component\Translation\Loader\MoFileLoader</span>
        <span class="hljs-attr">ext:</span> <span class="hljs-string">[mo]</span></code></pre>
<p>Each translation format defines:</p>
<ul>
<li><code translate="no">loader</code>: Symfony translation loader class</li>
<li><code translate="no">ext</code>: one or more file extensions associated with this format</li>
</ul>
<h2 id="layouts-components">layouts.components</h2>
<p><a href="/templates/components/">Templates Components</a> options.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">layouts:</span>
  <span class="hljs-attr">components:</span>
    <span class="hljs-attr">dir:</span> <span class="hljs-string">components</span> <span class="hljs-comment"># components source directory (`components` by default)</span>
    <span class="hljs-attr">ext:</span> <span class="hljs-string">twig</span>       <span class="hljs-comment"># components files extension (`twig` by default)</span></code></pre>
<h2 id="layouts-sections">layouts.sections</h2>
<p>Maps a section to the layouts of another section: the mapped name is used in place of <code translate="no">&lt;section&gt;</code> by the <a href="/templates/lookup-rules/#lookup-rules">lookup rules</a> of the section and of its pages.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">layouts:</span>
  <span class="hljs-attr">sections:</span>
    <span class="hljs-attr">news:</span> <span class="hljs-string">blog</span> <span class="hljs-comment"># the "news" section is rendered with `blog/list.html.twig` and its pages with `blog/page.html.twig`</span></code></pre>
<aside class="note note-tip"><p>A <a href="/content/pages/#sub-section">sub-section</a> already falls back to the layouts of its parent sections: no mapping is needed for that.</p></aside>
<hr>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/templates/extend/</id>
    <title>Extend</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/templates/extend/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Extend</h1>
<h2 id="functions-and-filters">Functions and filters</h2>
<p>You can add custom <a href="/documentation/reference/functions/">functions</a> and custom <a href="/documentation/reference/filters/">filters</a> with a <a href="/developers/extend/#twig-extension"><strong><em>Twig extension</em></strong></a>.</p>
<h2 id="theme">Theme</h2>
<p>It’s easy to build a theme, you just have to create a folder <code translate="no">&lt;theme&gt;</code> with the following structure (like a website but without pages):</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">&lt;mywebsite&gt;
└─ themes
   └─ &lt;theme&gt;
      ├─ config.yml
      ├─ assets
      ├─ layouts
      ├─ static
      └─ translations</code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/output/</id>
    <title>Output</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/output/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Output</h1>
<p>Defines where and in what format pages are rendered.</p>
<h2 id="output-dir">output.dir</h2>
<p>Directory where rendered pages’ files are saved (<code translate="no">_site</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">output:</span>
  <span class="hljs-attr">dir:</span> <span class="hljs-string">_site</span></code></pre>
<h2 id="output-formats">output.formats</h2>
<p>List of output formats definition, which are used to render pages (e.g. HTML, Atom, RSS, JSON, XML, etc.).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">output:</span>
  <span class="hljs-attr">formats:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">&lt;name&gt;</span>            <span class="hljs-comment"># name of the format, e.g.: `html` (required)</span>
      <span class="hljs-attr">mediatype:</span> <span class="hljs-string">&lt;media</span> <span class="hljs-string">type&gt;</span> <span class="hljs-comment"># media type (MIME type), ie: 'text/html' (optional)</span>
      <span class="hljs-attr">subpath:</span> <span class="hljs-string">&lt;sub</span> <span class="hljs-string">path&gt;</span>     <span class="hljs-comment"># sub path, e.g.: `amp` in `path/amp/index.html` (optional)</span>
      <span class="hljs-attr">filename:</span> <span class="hljs-string">&lt;file</span> <span class="hljs-string">name&gt;</span>   <span class="hljs-comment"># file name, e.g.: `index` in `path/index.html` (optional)</span>
      <span class="hljs-attr">extension:</span> <span class="hljs-string">&lt;extension&gt;</span>  <span class="hljs-comment"># file extension, e.g.: `html` in `path/index.html` (required)</span>
      <span class="hljs-attr">exclude:</span> <span class="hljs-string">[&lt;variable&gt;]</span>   <span class="hljs-comment"># don’t apply this format to pages identified by listed variables, e.g.: `[redirect, paginated]` (optional)</span></code></pre>
<p>Those formats are used in the <a href="#output-pagetypeformats"><code translate="no">output.pagetypeformats</code></a> configuration and in the <a href="/content/front-matter/#output"><code translate="no">output</code> page variable</a>.</p>
<h3 id="default-formats">Default formats</h3>
<p>Cecil provides some <a href="https://github.com/Cecilapp/Cecil/blob/main/config/base.php#L81-L162" target="_blank" rel="noopener noreferrer">default formats</a>, which can be overridden in the configuration file: <code translate="no">html</code> (default), <code translate="no">atom</code>, <code translate="no">rss</code>, <code translate="no">json</code>, <code translate="no">xml</code>, <code translate="no">txt</code>, <code translate="no">amp</code>, <code translate="no">js</code>, <code translate="no">webmanifest</code>, <code translate="no">xsl</code>, <code translate="no">jsonfeed</code>, <code translate="no">iframe</code>, <code translate="no">oembed</code>.</p>
<h2 id="output-pagetypeformats">output.pagetypeformats</h2>
<p>It’s not required to set <code translate="no">output</code> variable for each page, as Cecil automatically applies the formats defined for each page type (<code translate="no">homepage</code>, <code translate="no">page</code>, <code translate="no">section</code>, <code translate="no">vocabulary</code> and <code translate="no">term</code>).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">output:</span>
  <span class="hljs-attr">pagetypeformats:</span>
    <span class="hljs-attr">page:</span> <span class="hljs-string">[&lt;format&gt;]</span>
    <span class="hljs-attr">homepage:</span> <span class="hljs-string">[&lt;format&gt;]</span>
    <span class="hljs-attr">section:</span> <span class="hljs-string">[&lt;format&gt;]</span>
    <span class="hljs-attr">vocabulary:</span> <span class="hljs-string">[&lt;format&gt;]</span>
    <span class="hljs-attr">term:</span> <span class="hljs-string">[&lt;format&gt;]</span></code></pre>
<p>Several formats can be defined for the one type of page. For example the <code translate="no">section</code> page type can be automatically rendered in HTML and Atom:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">output:</span>
  <span class="hljs-attr">pagetypeformats:</span>
    <span class="hljs-attr">section:</span> <span class="hljs-string">[html,</span> <span class="hljs-string">atom]</span></code></pre>
<aside class="note note-info"><p>To render a page, <a href="/templates/lookup-rules/#lookup-rules">Cecil lookup for a template</a> named <code translate="no">&lt;layout&gt;.&lt;format&gt;.twig</code> (e.g. <code translate="no">page.html.twig</code>)</p></aside>
<h2 id="output-example">output example</h2>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">output:</span>
  <span class="hljs-attr">dir:</span> <span class="hljs-string">_site</span>
  <span class="hljs-attr">formats:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">html</span>
      <span class="hljs-attr">mediatype:</span> <span class="hljs-string">text/html</span>
      <span class="hljs-attr">filename:</span> <span class="hljs-string">index</span>
      <span class="hljs-attr">extension:</span> <span class="hljs-string">html</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">atom</span>
      <span class="hljs-attr">mediatype:</span> <span class="hljs-string">application/xml</span>
      <span class="hljs-attr">filename:</span> <span class="hljs-string">atom</span>
      <span class="hljs-attr">extension:</span> <span class="hljs-string">xml</span>
      <span class="hljs-attr">exclude:</span> <span class="hljs-string">[redirect,</span> <span class="hljs-string">paginated]</span>
  <span class="hljs-attr">pagetypeformats:</span>
    <span class="hljs-attr">page:</span> <span class="hljs-string">[html]</span>
    <span class="hljs-attr">homepage:</span> <span class="hljs-string">[html,</span> <span class="hljs-string">atom]</span>
    <span class="hljs-attr">section:</span> <span class="hljs-string">[html,</span> <span class="hljs-string">atom]</span>
    <span class="hljs-attr">vocabulary:</span> <span class="hljs-string">[html]</span>
    <span class="hljs-attr">term:</span> <span class="hljs-string">[html,</span> <span class="hljs-string">atom]</span></code></pre>
<h2 id="post-process">Post process</h2>
<p>You can extend Cecil capabilities with an <a href="/developers/extend/#output-post-processor">Output post processor</a> to modify the output files after they have been generated.</p>
<hr>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/cache/</id>
    <title>Cache</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/cache/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Cache</h1>
<p>Cache options.</p>
<h2 id="cache-enabled">cache.enabled</h2>
<p>Cache is enabled by default (<code translate="no">true</code>), but you can disable it with:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">cache:</span>
  <span class="hljs-attr">enabled:</span> <span class="hljs-literal">false</span></code></pre>
<aside class="note note-warning"><p>It’s not recommended to disable the cache for performance reasons.</p></aside>
<h2 id="cache-dir">cache.dir</h2>
<p>Directory where cache files are stored (<code translate="no">.cache</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">cache:</span>
  <span class="hljs-attr">dir:</span> <span class="hljs-string">'.cache'</span></code></pre>
<aside class="note note-info"><p>The cache directory is relative to the site directory, but you can use an absolute path: it can be useful to store the cache in a shared directory.</p></aside>
<h2 id="cache-assets">cache.assets</h2>
<p>Assets cache options.</p>
<h3 id="cache-assets-ttl">cache.assets.ttl</h3>
<p>Time to live of assets cache in seconds (<code translate="no">null</code> by default = no expiration).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">cache:</span>
  <span class="hljs-attr">assets:</span>
    <span class="hljs-attr">ttl:</span> <span class="hljs-string">~</span></code></pre>
<h3 id="cache-assets-remote-ttl">cache.assets.remote.ttl</h3>
<p>Time to live of remote assets cache in seconds (7 days by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">cache:</span>
  <span class="hljs-attr">assets:</span>
    <span class="hljs-attr">remotes:</span>
      <span class="hljs-attr">ttl:</span> <span class="hljs-number">604800</span> <span class="hljs-comment"># 7 days</span></code></pre>
<h2 id="cache-templates">cache.templates</h2>
<p>Disables templates cache with <code translate="no">false</code> (<code translate="no">true</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">cache:</span>
  <span class="hljs-attr">templates:</span> <span class="hljs-literal">true</span></code></pre>
<aside class="note note-info"><p>See <a href="/templates/cache/">templates cache documentation</a> for more details.</p></aside>
<h2 id="cache-translations">cache.translations</h2>
<p>Disables translations cache  with <code translate="no">false</code> (<code translate="no">true</code> by default).</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">cache:</span>
  <span class="hljs-attr">translations:</span> <span class="hljs-literal">true</span></code></pre>
<hr>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/server/</id>
    <title>Server and optimization</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/server/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Server and optimization</h1>
<h2 id="server">Server</h2>
<h3 id="server-headers">server.headers</h3>
<p>You can define custom <a href="https://developer.mozilla.org/docs/Glossary/Response_header" target="_blank" rel="noopener noreferrer">HTTP headers</a>, used by the local preview server.</p>
<aside class="note note-warning"><p>Since version <ins>8.38.0</ins>, the <code translate="no">headers</code> option has been moved to the <code translate="no">server.headers</code> section.</p></aside>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">server:</span>
  <span class="hljs-attr">headers:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">&lt;path&gt;</span> <span class="hljs-comment"># Relative path, prefixed with a slash. Support "*" wildcard.</span>
      <span class="hljs-attr">headers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">&lt;key&gt;</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"&lt;value&gt;"</span></code></pre>
<aside class="note note-tip"><p>It's useful to test custom <a href="https://developer.mozilla.org/docs/Web/HTTP/CSP" target="_blank" rel="noopener noreferrer">Content Security Policy</a> or <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control" target="_blank" rel="noopener noreferrer">Cache-Control</a>.</p></aside>
<p><em>Example:</em></p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">server:</span>
  <span class="hljs-attr">headers:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">/*</span>
      <span class="hljs-attr">headers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">X-Frame-Options</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"SAMEORIGIN"</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">X-XSS-Protection</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"1; mode=block"</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">X-Content-Type-Options</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"nosniff"</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">Content-Security-Policy</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"default-src 'self'; object-src 'self'; img-src 'self'"</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">Strict-Transport-Security</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"max-age=31536000; includeSubDomains; preload"</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">/assets/*</span>
      <span class="hljs-attr">headers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">Cache-Control</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"public, max-age=31536000"</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">/foo.html</span>
      <span class="hljs-attr">headers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">Foo</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"bar"</span></code></pre>
<hr>
<h2 id="optimize">Optimize</h2>
<p>The optimization options allow to enable compression of output files: HTML, CSS, JavaScript and image.</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">optimize:</span>
  <span class="hljs-attr">enabled:</span> <span class="hljs-literal">false</span>     <span class="hljs-comment"># enables files optimization (`false` by default)</span>
  <span class="hljs-attr">html:</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>    <span class="hljs-comment"># enables HTML files optimization</span>
    <span class="hljs-attr">ext:</span> <span class="hljs-string">[html,</span> <span class="hljs-string">htm]</span>   <span class="hljs-comment"># supported files extensions</span>
  <span class="hljs-attr">css:</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>    <span class="hljs-comment"># enables CSS files optimization</span>
    <span class="hljs-attr">ext:</span> <span class="hljs-string">[css]</span>         <span class="hljs-comment"># supported files extensions</span>
  <span class="hljs-attr">js:</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>    <span class="hljs-comment"># enables JavaScript files optimization</span>
    <span class="hljs-attr">ext:</span> <span class="hljs-string">[js]</span>          <span class="hljs-comment"># supported files extensions</span>
  <span class="hljs-attr">images:</span>
    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>    <span class="hljs-comment"># enables images files optimization</span>
    <span class="hljs-attr">ext:</span> <span class="hljs-string">[jpeg,</span> <span class="hljs-string">jpg,</span> <span class="hljs-string">png,</span> <span class="hljs-string">gif,</span> <span class="hljs-string">webp,</span> <span class="hljs-string">svg,</span> <span class="hljs-string">avif]</span> <span class="hljs-comment"># supported files extensions</span></code></pre>
<p>This option is disabled by default and can be enabled via:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">optimize:</span> <span class="hljs-literal">true</span></code></pre>
<p>Once the global option is enabled, the 4 file types will be processed.<br>
It is possible to disable each of them via <code translate="no">enabled: false</code> and modify processed files extension via <code translate="no">ext</code>.</p>
<aside class="note note-tip"><p>It is also possible to enable this option through CLI when using the "build" and "serve" commands via the <code translate="no">--optimize</code> option.</p></aside>
<aside class="note note-important"><p><strong>Images</strong> compressor will use these binaries if they are present in the system: <a href="https://github.com/tjko/jpegoptim" target="_blank" rel="noopener noreferrer">JpegOptim</a>, <a href="http://optipng.sourceforge.net/" target="_blank" rel="noopener noreferrer">Optipng</a>, <a href="https://pngquant.org/" target="_blank" rel="noopener noreferrer">Pngquant 2</a>, <a href="https://github.com/svg/svgo" target="_blank" rel="noopener noreferrer">SVGO</a>, <a href="http://www.lcdf.org/gifsicle/" target="_blank" rel="noopener noreferrer">Gifsicle</a>, <a href="https://developers.google.com/speed/webp/docs/cwebp" target="_blank" rel="noopener noreferrer">cwebp</a> and <a href="https://github.com/AOMediaCodec/libavif" target="_blank" rel="noopener noreferrer">avifenc</a>.</p></aside>
<hr>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/override/</id>
    <title>Override configuration</title>
    <published>2021-05-07T00:00:00+00:00</published>
    <updated>2026-10-05T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/override/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Override configuration</h1>
<h2 id="environment-variables">Environment variables</h2>
<p>The configuration can be overridden through <a href="https://en.wikipedia.org/wiki/Environment_variable" target="_blank" rel="noopener noreferrer">environment variables</a>.</p>
<p>At startup, Cecil also attempts to load a <code translate="no">.env</code> file from the current site path (the current working directory, or the <code translate="no">&lt;path&gt;</code> argument if provided).</p>
<ul>
<li>If the <code translate="no">.env</code> file does not exist, Cecil continues normally.</li>
<li>Variables already defined by the shell/system are preserved.</li>
</ul>
<p>Each environment variable name must be prefixed with <code translate="no">CECIL_</code> and the configuration key must be set in uppercase.</p>
<p>For example, the following command set the website’s <code translate="no">baseurl</code>:</p>
<pre><code class="language-bash hljs bash" translate="no"><span class="hljs-built_in">export</span> CECIL_BASEURL=<span class="hljs-string">"https://example.com/"</span></code></pre>
<p>You can store the same value in a <code translate="no">.env</code> file at your project root:</p>
<pre><code class="language-dotenv" translate="no">CECIL_BASEURL="https://example.com/"
CECIL_TITLE="My Cecil site"</code></pre>
<h2 id="cli-option">CLI option</h2>
<p>You can combine multiple configuration files, with the <code translate="no">--config</code> option (left-to-right precedence):</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar --config config-1.yml,config-2.yml</code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/commands/new-site/</id>
    <title>new:site</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/commands/new-site/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>new:site</h1>
<p>Creates a new site.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">Description:
  Creates a new website

Usage:
  new:site [options] [--] [&lt;path&gt;]

Arguments:
  path                  Use the given path as working directory

Options:
  -f, --force           Override directory if it already exists
      --demo            Add demo content (pages, templates and assets)
  -h, --help            Display help for the given command. When no command is given display help for the list command
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  The new:site command creates a new website in the current directory, or in &lt;path&gt; if provided.
  If you run this command without any options, it will ask you for the website title, baseline, base URL, description, etc.

    cecil.phar new:site
    cecil.phar new:site path/to/the/working/directory

  To create a new website with demo content, run:

    cecil.phar new:site --demo

  To override an existing website, run:

    cecil.phar new:site --force</code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/deploy/jamstack-platforms/</id>
    <title>Jamstack platforms</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/deploy/jamstack-platforms/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Jamstack platforms</h1>
<h2 id="netlify">Netlify</h2>
<blockquote>
<p>A powerful serverless platform with an intuitive git-based workflow. Automated deployments, shareable previews, and much more.</p>
</blockquote>
<p>➡️ <a href="https://www.netlify.com" target="_blank" rel="noopener noreferrer">https://www.netlify.com</a></p>
<p><em>netlify.toml</em>:</p>
<pre><code class="language-bash hljs bash" translate="no">[build]
  publish = <span class="hljs-string">"_site"</span>
  <span class="hljs-built_in">command</span> = <span class="hljs-string">"curl -sSOL https://cecil.app/build.sh &amp;&amp; bash ./build.sh"</span>
[context.production.environment]
  CECIL_ENV = <span class="hljs-string">"production"</span>
[context.deploy-preview.environment]
  CECIL_ENV = <span class="hljs-string">"preview"</span></code></pre>
<p><a href="https://www.netlify.com/docs/continuous-deployment/" target="_blank" rel="noopener noreferrer">Official documentation</a></p>
<h2 id="vercel">Vercel</h2>
<blockquote>
<p>Vercel combines the best developer experience with an obsessive focus on end-user performance.</p>
</blockquote>
<p>➡️ <a href="https://vercel.com" target="_blank" rel="noopener noreferrer">https://vercel.com</a></p>
<p><em>vercel.json</em>:</p>
<pre><code class="language-json hljs json" translate="no">{
  <span class="hljs-attr">"buildCommand"</span>: <span class="hljs-string">"curl -sSOL https://cecil.app/build.sh &amp;&amp; bash ./build.sh"</span>,
  <span class="hljs-attr">"outputDirectory"</span>: <span class="hljs-string">"_site"</span>
}</code></pre>
<p><a href="https://vercel.com/docs/concepts/deployments/build-step#build-command" target="_blank" rel="noopener noreferrer">Official documentation</a></p>
<h2 id="statichost">statichost</h2>
<blockquote>
<p>Modern static site hosting with European servers and absolutely no personal data collection!</p>
</blockquote>
<p>➡️ <a href="https://statichost.eu" target="_blank" rel="noopener noreferrer">https://statichost.eu</a></p>
<p><em>statichost.yml</em>:</p>
<pre><code class="language-yml hljs yaml" translate="no"><span class="hljs-attr">image:</span> <span class="hljs-string">wordpress:cli-php8.4</span>
<span class="hljs-attr">command:</span> <span class="hljs-string">curl</span> <span class="hljs-string">-sSOL</span> <span class="hljs-string">https://cecil.app/build.sh</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">bash</span> <span class="hljs-string">./build.sh</span>
<span class="hljs-attr">public:</span> <span class="hljs-string">_site</span></code></pre>
<p><a href="https://www.statichost.eu/docs/" target="_blank" rel="noopener noreferrer">Official documentation</a></p>
<h2 id="cloudflare-pages">Cloudflare Pages</h2>
<aside class="note note-caution"><p>Cloudflare Pages no longer supports PHP.</p></aside>
<blockquote>
<p>Cloudflare Pages is a JAMstack platform for frontend developers to collaborate and deploy websites.</p>
</blockquote>
<p>➡️ <a href="https://pages.cloudflare.com" target="_blank" rel="noopener noreferrer">https://pages.cloudflare.com</a></p>
<p>Build configurations:</p>
<ul>
<li>Framework preset: <code translate="no">None</code></li>
<li>Build command: <code translate="no">curl -sSOL https://cecil.app/build.sh &amp;&amp; bash ./build.sh</code></li>
<li>Build output directory: <code translate="no">_site</code></li>
</ul>
<p><a href="https://developers.cloudflare.com/pages/" target="_blank" rel="noopener noreferrer">Official documentation</a></p>
<h2 id="render">Render</h2>
<aside class="note note-caution"><p>Render no longer supports PHP.</p></aside>
<blockquote>
<p>Render is a unified cloud to build and run all your apps and websites with free TLS certificates, global CDN, private networks and auto deploys from Git.</p>
</blockquote>
<p>➡️ <a href="https://render.com" target="_blank" rel="noopener noreferrer">https://render.com</a></p>
<p><em>render.yaml</em>:</p>
<pre><code class="language-yml hljs yaml" translate="no"><span class="hljs-attr">previewsEnabled:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">type:</span> <span class="hljs-string">web</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Cecil</span>
    <span class="hljs-attr">env:</span> <span class="hljs-string">static</span>
    <span class="hljs-attr">buildCommand:</span> <span class="hljs-string">curl</span> <span class="hljs-string">-sSOL</span> <span class="hljs-string">https://cecil.app/build.sh</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">bash</span> <span class="hljs-string">./build.sh</span>
    <span class="hljs-attr">staticPublishPath:</span> <span class="hljs-string">_site</span>
    <span class="hljs-attr">pullRequestPreviewsEnabled:</span> <span class="hljs-literal">true</span></code></pre>
<p><a href="https://render.com/docs/static-sites" target="_blank" rel="noopener noreferrer">Official documentation</a></p>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/getting-started/quick-start/</id>
    <title>Quick Start</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/getting-started/quick-start/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Quick Start</h1>
<h2 id="create-a-website">Create a website</h2>
<p>You can create a new website from scratch in a few minutes.</p>
<p>Follow the steps below to create your first Cecil website.</p>
<p><a href="https://cecilapp.github.io/skeleton/" target="_blank" rel="noopener noreferrer"><picture>
<source media="(prefers-color-scheme: dark)" type="image/avif" srcset="/thumbnails/480x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.avif 480w, /thumbnails/768x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.avif 768w, /docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.avif 1007w" sizes="100vw">
<source media="(prefers-color-scheme: dark)" type="image/webp" srcset="/thumbnails/480x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.webp 480w, /thumbnails/768x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.webp 768w, /docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.webp 1007w" sizes="100vw">
<source media="(prefers-color-scheme: dark)" srcset="/thumbnails/480x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.png 480w, /thumbnails/768x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.png 768w, /docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.png 1007w" sizes="100vw">
<source type="image/avif" srcset="/thumbnails/480x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.avif 480w, /thumbnails/768x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.avif 768w, /docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.avif 1007w" width="1007" height="607" sizes="100vw">
<source type="image/webp" srcset="/thumbnails/480x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.webp 480w, /thumbnails/768x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.webp 768w, /docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.webp 1007w" width="1007" height="607" sizes="100vw">
<img src="/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.png" alt="New website example" loading="lazy" decoding="async" class="dark:brightness-90" width="1007" height="607" style=";max-width:100%;height:auto;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAyCAYAAACqNX6+AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEj0lEQVR4nN1b0ZKDIAxMHP//H+9HLvcg0QQCRATE2xmLrQghy4ZIW/z5+SEAgN/fXyAio6SjpKPk4wQRUCgBIJyDBgIgIiAibNvmOBA23AA3hA3xaKJSJh02IX/f0Q0Cd1ezxzTLgb3tNgAAShxvkjEYZPbnMcLyGBmORHW9nWwfmglRjmCVvALZ8x1n2ffFBCOOJ0HigUJAhyl10g85d6yrjOATYYodVm08IwTGkVF3wf9SBuMRIQQI5wDF6Uj8V2UwDELYs5h8alb1MCGyrMMD9bmP0Zvrs/+pDIYghPM5AiA8/ZwlGVE4CMuTCUN9cXiJOZtIkzr4ijLIkHVOPftVIZhBePKRdzDKOaYKu354iUipEiO45lcUrz6sqYyYJCZol0YgXOI4P1B3HRV0kPIRghlCcsSo/I1iQ54oo+T095TB2PV11OZsod/zwEtJkWEehcin2SIpFK8WlEi27javMqoNdUVOGYxLIUEZtn0IiAREmozoKSSLM8yEUGcSgSjCU2h3mjLuYIwyGJdC1NqBR4/hOBZUUgvr3YjL7cpSkSKmw7kFg3DYkMpSt6vwTWUwToUcixqAzJgQECgoA4EuH4nSAzxLvEq8Sukdq4+zb7Kv614kvqMMxs6TTxPBbgC+AgSoHAFgu6AEuQLw7ql1tYaYjHjF+aIyGPrBEMMsRIQrCzzmZ0xGK1TaWsytU+gJkp7LmvjYUt2rTxliyjV2v8MlEWWABCKGsNVpd0Tk1WmK7c+fUpWkdfyt9kWrMg+FKE+jLjhuI3VRyNULyjfFlq1JUFPJczvnKoMhHgz18njGaQ5jYg3phgdKqakkp5zReLpmqb0snqg6oyG5tdV9gCWl1MgvqaTdTqkMzkCjGpR6oleykN9+P0c4YY4VlZJHSSVzd6b6ZXN7rIxrmlEyorEDFMnrjY5yKjFDmYdktWbE18Ypg5EqZKYyrL49nwmUyFCrI97Osl3o/Zyzs4HUJwAPWvTv3WI/OJZRVobRz6D5einEkUJ1dXYJDYOtpcZJ8w8dOmoHIHlSf+r0aaQ5cfopyCZeIlVgeFEZjKYfOazmdImibXSpozVCj94byxLS6vSVyQKAbAxTa88LymBUFVJy8MrOrymlJAvr0qxdY3fI8jh/ZYJyUNkYGXtik7P/hJA4bYxLy+mrEvHULit8TVvUvRuHJWLA2cYKqO0Wy3ozoRRSU4c1CDmQFcnoZdMsYvaWLfUvEmNhJWUwdu7c2gfyqkSer0hGj7VkFm6vIVb9rxDjwVvKYDStId4t7y8R8zYRjO2JIXjz/E1Q5lgNyRrSgppKViHlC7j9YHgnfK2IVe1iNC/qMXJqWNUBKI6VsFtMtKjEun9lxISsQsxeCjke50t8IWRJWISgccxE8svFpwbw/SsrxMoCS0qZ/mCYCzEeQ+I6UxXSoZMcIUlIm/0FVfWvd07IZrqppLMzSip4e01xfUFlkZUz+P531Q1D7+itXFaY62L49yGef/jwr7zvhLCyOpyjGjj4rMPHdemCUyH3zfyCOp40P+w79ZKzrf/M9cP7Knmxqyz+AEm28LzQnWd/AAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center;background-size:cover;" srcset="/thumbnails/480x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.png 480w, /thumbnails/768x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.png 768w, /docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.png 1007w" sizes="100vw">
</picture></a></p>
<aside class="note note-info"><p>Demo of the expected result: <a href="https://cecilapp.github.io/skeleton/" target="_blank" rel="noopener noreferrer">https://cecilapp.github.io/skeleton/</a>.</p></aside>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li><a href="https://php.net/manual/en/install.php" target="_blank" rel="noopener noreferrer">PHP</a> 8.3+</li>
<li>Terminal (a basic understanding of <a href="https://wikipedia.org/wiki/Terminal_emulator" target="_blank" rel="noopener noreferrer">terminal</a>)</li>
<li>Text editor, like <a href="https://code.visualstudio.com" target="_blank" rel="noopener noreferrer">VS Code</a> and/or <a href="https://typora.io" target="_blank" rel="noopener noreferrer">Typora</a></li>
</ul>
<h3 id="1-download-cecil">1. Download Cecil</h3>
<p>Download <code translate="no">cecil.phar</code> from your terminal:</p>
<pre><code class="language-bash hljs bash" translate="no">curl -LO https://cecil.app/cecil.phar</code></pre>
<p>You can also <a href="https://cecil.app/download/">download Cecil manually</a>, or use:</p>
<ul>
<li><a href="https://brew.sh" target="_blank" rel="noopener noreferrer">Homebrew</a>: <code translate="no">brew install cecilapp/tap/cecil</code></li>
<li><a href="https://scoop.sh" target="_blank" rel="noopener noreferrer">Scoop</a>: <code translate="no">scoop install https://cecil.app/scoop/cecil.json</code></li>
</ul>
<h3 id="2-create-a-new-site">2. Create a new site</h3>
<p>Create a directory for the website (e.g.: <code translate="no">&lt;mywebsite&gt;</code>), put <code translate="no">cecil.phar</code> in it, then run the <code translate="no">new:site</code> command:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar new:site</code></pre>
<h3 id="3-add-a-page">3. Add a page</h3>
<p>Run the <code translate="no">new:page</code> command:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar new:page</code></pre>
<p>Now you can edit the newly created page with your Markdown editor: <code translate="no">&lt;mywebsite&gt;/pages/&lt;new-page&gt;.md</code>.</p>
<aside class="note note-tip"><p>We recommend you to use <a href="https://www.typora.io" target="_blank" rel="noopener noreferrer">Typora</a> to edit your Markdown files.</p></aside>
<h3 id="4-check-the-preview">4. Check the preview</h3>
<p>Run the following command to create a preview of the website:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar serve</code></pre>
<p>Then navigate to <code translate="no">http://localhost:8000</code>.</p>
<aside class="note note-info"><p>The <code translate="no">serve</code> command runs a local HTTP server and a watcher: if a file (a page, a template or the config) is modified, the browser’s current page is automatically reloaded.</p></aside>
<h3 id="5-build-and-deploy">5. Build and deploy</h3>
<p>When you are satisfied with the result, you can generate the website in order to deploy it on the Web.</p>
<p>Run the following command to build the website:</p>
<pre><code class="language-bash hljs bash" translate="no">php cecil.phar build</code></pre>
<p>You can now copy the content of the <code translate="no">_site</code> directory to a Web server 🎉</p>
<hr>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/commands/new-page/</id>
    <title>new:page</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/commands/new-page/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>new:page</h1>
<p>Creates a new page.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">Description:
  Creates a new page

Usage:
  new:page [options] [--] [&lt;path&gt;]

Arguments:
  path                        Use the given path as working directory

Options:
      --name=NAME             Page path name
      --slugify|--no-slugify  Slugify file name (or disable --no-slugify)
  -p, --prefix                Prefix the file name with the current date (`YYYY-MM-DD`)
  -f, --force                 Override the file if already exist
  -o, --open                  Open editor automatically
      --editor=EDITOR         Editor to use with open option
  -h, --help                  Display help for the given command. When no command is given display help for the list command
  -q, --quiet                 Do not output any message
  -V, --version               Display this application version
      --ansi|--no-ansi        Force (or disable --no-ansi) ANSI output
  -n, --no-interaction        Do not ask any interactive question
  -v|vv|vvv, --verbose        Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  The new:page command creates a new page file.
  If you run this command without any options, it will ask you for the page name and other options.

    cecil.phar new:page
    cecil.phar new:page --name=path/to/a-page.md
    cecil.phar new:page --name=path/to/A Page.md --slugify

  To create a new page with a date prefix (i.e: `YYYY-MM-DD`), run:

    cecil.phar new:page --prefix

  To create a new page and open it with an editor, run:

    cecil.phar new:page --open --editor=editor

  To override an existing page, run:

    cecil.phar new:page --force</code></pre>
<h2 id="page-s-models">Page’s models</h2>
<p>You can define your own models for your new pages in the <code translate="no">models</code> directory:</p>
<ol>
<li>The name must be based on the section’s name (e.g.: <code translate="no">blog.md</code>)</li>
<li>The default model must be named <code translate="no">default.md</code> (for root pages or pages’s section without model)</li>
</ol>
<p>Two dynamic variables are available:</p>
<ol>
<li><code translate="no">%title%</code>: the file’s name</li>
<li><code translate="no">%date%</code>: the current date</li>
</ol>
<h2 id="open-with-your-editor">Open with your editor</h2>
<p>With the <code translate="no">--open</code> option, the editor will be opened automatically. So use <code translate="no">editor</code> key in your configuration file to define the default editor (e.g.: <code translate="no">editor: typora</code>).</p>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/deploy/continuous-deployment/</id>
    <title>Continuous build &amp; deploy</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/deploy/continuous-deployment/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Continuous build &amp; deploy</h1>
<h2 id="github-pages">GitHub Pages</h2>
<blockquote>
<p>Websites for you and your projects, hosted directly from your GitHub repository. Just edit, push, and your changes are live.</p>
</blockquote>
<p>➡️ <a href="https://pages.github.com" target="_blank" rel="noopener noreferrer">https://pages.github.com</a></p>
<p><em>.github/workflows/build-and-deploy.yml</em>:</p>
<pre><code class="language-yml hljs yaml" translate="no"><span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">and</span> <span class="hljs-string">deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> <span class="hljs-string">[master,</span> <span class="hljs-string">main]</span>
  <span class="hljs-attr">workflow_dispatch:</span>
<span class="hljs-attr">concurrency:</span>
  <span class="hljs-attr">group:</span> <span class="hljs-string">pages</span>
  <span class="hljs-attr">cancel-in-progress:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">source</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v6</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Restore</span> <span class="hljs-string">Cecil</span> <span class="hljs-string">cache</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache/restore@v5</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">./.cache</span>
          <span class="hljs-attr">key:</span> <span class="hljs-string">cecil-cache-</span>
          <span class="hljs-attr">restore-keys:</span> <span class="hljs-string">|
            cecil-cache-
</span>      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">site</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">Cecilapp/Cecil-Action@v4</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Save</span> <span class="hljs-string">Cecil</span> <span class="hljs-string">cache</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache/save@v5</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">./.cache</span>
          <span class="hljs-attr">key:</span> <span class="hljs-string">cecil-cache-${{</span> <span class="hljs-string">hashFiles('./.cache/**/*')</span> <span class="hljs-string">}}</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">build</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">pages:</span> <span class="hljs-string">write</span>
      <span class="hljs-attr">id-token:</span> <span class="hljs-string">write</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">github-pages</span>
      <span class="hljs-attr">url:</span> <span class="hljs-string">${{</span> <span class="hljs-string">steps.deployment.outputs.page_url</span> <span class="hljs-string">}}</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Pages</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">deployment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/deploy-pages@v5</span></code></pre>
<p><a href="https://docs.github.com/en/pages" target="_blank" rel="noopener noreferrer">Official documentation</a></p>
<h2 id="gitlab-ci">GitLab CI</h2>
<blockquote>
<p>With GitLab Pages, you can publish static websites directly from a repository in GitLab.</p>
</blockquote>
<p>➡️ <a href="https://about.gitlab.com/solutions/continuous-integration/" target="_blank" rel="noopener noreferrer">https://about.gitlab.com/solutions/continuous-integration/</a></p>
<p><em>.gitlab-ci.yml</em>:</p>
<pre><code class="language-yml hljs yaml" translate="no"><span class="hljs-attr">image:</span> <span class="hljs-string">wordpress:cli-php8.4</span>
<span class="hljs-attr">test:</span>
  <span class="hljs-attr">stage:</span> <span class="hljs-string">test</span>
  <span class="hljs-attr">variables:</span>
    <span class="hljs-attr">CECIL_OUTPUT_DIR:</span> <span class="hljs-string">test</span>
  <span class="hljs-attr">script:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">curl</span> <span class="hljs-string">-sSOL</span> <span class="hljs-string">https://cecil.app/build.sh</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">bash</span> <span class="hljs-string">./build.sh</span>
  <span class="hljs-attr">artifacts:</span>
    <span class="hljs-attr">paths:</span>
     <span class="hljs-bullet">-</span> <span class="hljs-string">test</span>
  <span class="hljs-attr">except:</span>
   <span class="hljs-bullet">-</span> <span class="hljs-string">master</span>
<span class="hljs-attr">pages:</span>
  <span class="hljs-attr">stage:</span> <span class="hljs-string">deploy</span>
  <span class="hljs-attr">variables:</span>
    <span class="hljs-attr">CECIL_ENV:</span> <span class="hljs-string">production</span>
    <span class="hljs-attr">CECIL_OUTPUT_DIR:</span> <span class="hljs-string">public</span>
  <span class="hljs-attr">script:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">curl</span> <span class="hljs-string">-sSOL</span> <span class="hljs-string">https://cecil.app/build.sh</span> <span class="hljs-string">&amp;&amp;</span> <span class="hljs-string">bash</span> <span class="hljs-string">./build.sh</span>
  <span class="hljs-attr">artifacts:</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">public</span>
  <span class="hljs-attr">only:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">master</span>
<span class="hljs-attr">cache:</span>
  <span class="hljs-attr">paths:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">composer-cache/</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">vendor/</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.cache/</span></code></pre>
<p><a href="https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/" target="_blank" rel="noopener noreferrer">Official documentation</a></p>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/commands/serve/</id>
    <title>serve</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/commands/serve/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>serve</h1>
<p>Builds and serves the site locally.</p>
<aside class="note note-warning"><p>The web server is designed to aid website testing. It is not intended to be a full-featured web server and it should not be used on a public network.</p></aside>
<pre><code class="language-plaintext hljs plaintext" translate="no">Description:
  Starts the built-in server

Usage:
  serve [options] [--] [&lt;path&gt;]

Arguments:
  path                             Use the given path as working directory

Options:
  -o, --open                       Open web browser automatically
      --host=HOST                  Server host [default: "localhost"]
      --port=PORT                  Server port [default: "8000"]
  -w, --watch|--no-watch           Enable (or disable --no-watch) changes watcher (enabled by default)
  -i, --incremental                Enable incremental builds (rebuild only changed pages)
  -d, --drafts                     Include drafts
      --optimize|--no-optimize     Enable (or disable --no-optimize) optimization of generated files
  -c, --config=CONFIG              Set the path to extra config files (comma-separated)
      --clear-cache[=CLEAR-CACHE]  Clear cache before build (optional cache key as regular expression) [default: false]
  -p, --page=PAGE                  Build a specific page
      --no-ignore-vcs              Changes watcher must not ignore VCS directories
  -m, --metrics                    Show build metrics (duration and memory) of each step
      --timeout=TIMEOUT            Sets the process timeout (max. runtime) in seconds [default: 7200]
      --notify                     Send desktop notification on server start
  -b, --background                 Run the server in the background
  -h, --help                       Display help for the given command. When no command is given display help for the list command
  -q, --quiet                      Do not output any message
  -V, --version                    Display this application version
      --ansi|--no-ansi             Force (or disable --no-ansi) ANSI output
  -n, --no-interaction             Do not ask any interactive question
  -v|vv|vvv, --verbose             Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  The serve command starts the live-reloading-built-in web server.

    cecil.phar serve
    cecil.phar serve path/to/the/working/directory
    cecil.phar serve --open
    cecil.phar serve --drafts
    cecil.phar serve --no-watch

  To speed up local development you can enable incremental builds with the --incremental option.
  When content pages change, Cecil rebuilds just those pages.
  When templates change, Cecil rebuilds only pages using those templates (including Twig dependencies such as extends/include).
  Any other change (data, config, static or asset file, or file deletion) triggers a full rebuild:

    cecil.phar serve --incremental

  You can use a custom host and port by using the --host and --port options:

    cecil.phar serve --host=127.0.0.1 --port=8080

  To build the website with an extra configuration file, you can use the --config option.
  This is useful during local development to override some settings without modifying the main configuration:

    cecil.phar serve --config=config/dev.yml

  To start the server with changes watcher not ignoring VCS directories, run:

    cecil.phar serve --no-ignore-vcs

  To define the process timeout (in seconds), run:

    cecil.phar serve --timeout=7200

  To run the server in the background, run:

    cecil.phar serve --background
    cecil.phar serve -b

  Then stop it with:

    cecil.phar serve:stop

  In background mode, file changes are not watched automatically.</code></pre>
<h2 id="serve-background">serve:background</h2>
<p>Alias of <code translate="no">serve --background</code>: starts the built-in server in the background without occupying the terminal.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">Description:
  Starts the built-in server in the background (alias of `serve --background`)

Usage:
  serve:background [options] [--] [&lt;path&gt;]

Help:
  The serve:background command starts the built-in web server in the background.

    cecil.phar serve:background
    cecil.phar serve:background path/to/the/working/directory

  This command is an alias of serve --background.

  Stop the server with:

    cecil.phar serve:stop</code></pre>
<h2 id="serve-log">serve:log</h2>
<p>Displays the combined server and error logs.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">Description:
  Shows combined server and error logs

Usage:
  serve:log [options] [--] [&lt;path&gt;]

Arguments:
  path                  Use the given path as working directory

Options:
  -l, --lines=LINES     Number of entries to display (default: 25)

Help:
  The serve:log command displays entries from combined server and error logs, sorted by date.

    cecil.phar serve:log
    cecil.phar serve:log path/to/the/working/directory
    cecil.phar serve:log --lines=100
    cecil.phar serve:log -l 100

  This command shows logs from `.cecil/errors.log` and `.cecil/server.log`.
  It is useful for debugging issues with your local development server.</code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/configuration/locale-codes/</id>
    <title>Locale codes</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/configuration/locale-codes/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Locale codes</h1>
<p>Available locale codes (<code translate="no">language_COUNTRY</code>) used by the <a href="/documentation/languages/#languages"><code translate="no">languages</code></a> option.</p>
<table>
<thead>
<tr>
<th>Locale</th>
<th>Code</th>
</tr>
</thead>
<tbody>
<tr>
<td>Albanian (Albania)</td>
<td>sq_AL</td>
</tr>
<tr>
<td>Albanian</td>
<td>sq</td>
</tr>
<tr>
<td>Arabic (Algeria)</td>
<td>ar_DZ</td>
</tr>
<tr>
<td>Arabic (Bahrain)</td>
<td>ar_BH</td>
</tr>
<tr>
<td>Arabic (Egypt)</td>
<td>ar_EG</td>
</tr>
<tr>
<td>Arabic (Iraq)</td>
<td>ar_IQ</td>
</tr>
<tr>
<td>Arabic (Jordan)</td>
<td>ar_JO</td>
</tr>
<tr>
<td>Arabic (Kuwait)</td>
<td>ar_KW</td>
</tr>
<tr>
<td>Arabic (Lebanon)</td>
<td>ar_LB</td>
</tr>
<tr>
<td>Arabic (Libya)</td>
<td>ar_LY</td>
</tr>
<tr>
<td>Arabic (Morocco)</td>
<td>ar_MA</td>
</tr>
<tr>
<td>Arabic (Oman)</td>
<td>ar_OM</td>
</tr>
<tr>
<td>Arabic (Qatar)</td>
<td>ar_QA</td>
</tr>
<tr>
<td>Arabic (Saudi Arabia)</td>
<td>ar_SA</td>
</tr>
<tr>
<td>Arabic (Sudan)</td>
<td>ar_SD</td>
</tr>
<tr>
<td>Arabic (Syria)</td>
<td>ar_SY</td>
</tr>
<tr>
<td>Arabic (Tunisia)</td>
<td>ar_TN</td>
</tr>
<tr>
<td>Arabic (United Arab Emirates)</td>
<td>ar_AE</td>
</tr>
<tr>
<td>Arabic (Yemen)</td>
<td>ar_YE</td>
</tr>
<tr>
<td>Arabic</td>
<td>ar</td>
</tr>
<tr>
<td>Belarusian (Belarus)</td>
<td>be_BY</td>
</tr>
<tr>
<td>Belarusian</td>
<td>be</td>
</tr>
<tr>
<td>Bulgarian (Bulgaria)</td>
<td>bg_BG</td>
</tr>
<tr>
<td>Bulgarian</td>
<td>bg</td>
</tr>
<tr>
<td>Catalan (Spain)</td>
<td>ca_ES</td>
</tr>
<tr>
<td>Catalan</td>
<td>ca</td>
</tr>
<tr>
<td>Chinese (China)</td>
<td>zh_CN</td>
</tr>
<tr>
<td>Chinese (Hong Kong)</td>
<td>zh_HK</td>
</tr>
<tr>
<td>Chinese (Singapore)</td>
<td>zh_SG</td>
</tr>
<tr>
<td>Chinese (Taiwan)</td>
<td>zh_TW</td>
</tr>
<tr>
<td>Chinese</td>
<td>zh</td>
</tr>
<tr>
<td>Croatian (Croatia)</td>
<td>hr_HR</td>
</tr>
<tr>
<td>Croatian</td>
<td>hr</td>
</tr>
<tr>
<td>Czech (Czech Republic)</td>
<td>cs_CZ</td>
</tr>
<tr>
<td>Czech</td>
<td>cs</td>
</tr>
<tr>
<td>Danish (Denmark)</td>
<td>da_DK</td>
</tr>
<tr>
<td>Danish</td>
<td>da</td>
</tr>
<tr>
<td>Dutch (Belgium)</td>
<td>nl_BE</td>
</tr>
<tr>
<td>Dutch (Netherlands)</td>
<td>nl_NL</td>
</tr>
<tr>
<td>Dutch</td>
<td>nl</td>
</tr>
<tr>
<td>English (Australia)</td>
<td>en_AU</td>
</tr>
<tr>
<td>English (Canada)</td>
<td>en_CA</td>
</tr>
<tr>
<td>English (India)</td>
<td>en_IN</td>
</tr>
<tr>
<td>English (Ireland)</td>
<td>en_IE</td>
</tr>
<tr>
<td>English (Malta)</td>
<td>en_MT</td>
</tr>
<tr>
<td>English (New Zealand)</td>
<td>en_NZ</td>
</tr>
<tr>
<td>English (Philippines)</td>
<td>en_PH</td>
</tr>
<tr>
<td>English (Singapore)</td>
<td>en_SG</td>
</tr>
<tr>
<td>English (South Africa)</td>
<td>en_ZA</td>
</tr>
<tr>
<td>English (United Kingdom)</td>
<td>en_GB</td>
</tr>
<tr>
<td>English (United States)</td>
<td>en_US</td>
</tr>
<tr>
<td>English</td>
<td>en</td>
</tr>
<tr>
<td>Estonian (Estonia)</td>
<td>et_EE</td>
</tr>
<tr>
<td>Estonian</td>
<td>et</td>
</tr>
<tr>
<td>Finnish (Finland)</td>
<td>fi_FI</td>
</tr>
<tr>
<td>Finnish</td>
<td>fi</td>
</tr>
<tr>
<td>French (Belgium)</td>
<td>fr_BE</td>
</tr>
<tr>
<td>French (Canada)</td>
<td>fr_CA</td>
</tr>
<tr>
<td>French (France)</td>
<td>fr_FR</td>
</tr>
<tr>
<td>French (Luxembourg)</td>
<td>fr_LU</td>
</tr>
<tr>
<td>French (Switzerland)</td>
<td>fr_CH</td>
</tr>
<tr>
<td>French</td>
<td>fr</td>
</tr>
<tr>
<td>German (Austria)</td>
<td>de_AT</td>
</tr>
<tr>
<td>German (Germany)</td>
<td>de_DE</td>
</tr>
<tr>
<td>German (Luxembourg)</td>
<td>de_LU</td>
</tr>
<tr>
<td>German (Switzerland)</td>
<td>de_CH</td>
</tr>
<tr>
<td>German</td>
<td>de</td>
</tr>
<tr>
<td>Greek (Cyprus)</td>
<td>el_CY</td>
</tr>
<tr>
<td>Greek (Greece)</td>
<td>el_GR</td>
</tr>
<tr>
<td>Greek</td>
<td>el</td>
</tr>
<tr>
<td>Hebrew (Israel)</td>
<td>iw_IL</td>
</tr>
<tr>
<td>Hebrew</td>
<td>iw</td>
</tr>
<tr>
<td>Hindi (India)</td>
<td>hi_IN</td>
</tr>
<tr>
<td>Hungarian (Hungary)</td>
<td>hu_HU</td>
</tr>
<tr>
<td>Hungarian</td>
<td>hu</td>
</tr>
<tr>
<td>Icelandic (Iceland)</td>
<td>is_IS</td>
</tr>
<tr>
<td>Icelandic</td>
<td>is</td>
</tr>
<tr>
<td>Indonesian (Indonesia)</td>
<td>in_ID</td>
</tr>
<tr>
<td>Indonesian</td>
<td>in</td>
</tr>
<tr>
<td>Irish (Ireland)</td>
<td>ga_IE</td>
</tr>
<tr>
<td>Irish</td>
<td>ga</td>
</tr>
<tr>
<td>Italian (Italy)</td>
<td>it_IT</td>
</tr>
<tr>
<td>Italian (Switzerland)</td>
<td>it_CH</td>
</tr>
<tr>
<td>Italian</td>
<td>it</td>
</tr>
<tr>
<td>Japanese (Japan)</td>
<td>ja_JP</td>
</tr>
<tr>
<td>Japanese (Japan,JP)</td>
<td>ja_JP_JP</td>
</tr>
<tr>
<td>Japanese</td>
<td>ja</td>
</tr>
<tr>
<td>Korean (South Korea)</td>
<td>ko_KR</td>
</tr>
<tr>
<td>Korean</td>
<td>ko</td>
</tr>
<tr>
<td>Latvian (Latvia)</td>
<td>lv_LV</td>
</tr>
<tr>
<td>Latvian</td>
<td>lv</td>
</tr>
<tr>
<td>Lithuanian (Lithuania)</td>
<td>lt_LT</td>
</tr>
<tr>
<td>Lithuanian</td>
<td>lt</td>
</tr>
<tr>
<td>Macedonian (Macedonia)</td>
<td>mk_MK</td>
</tr>
<tr>
<td>Macedonian</td>
<td>mk</td>
</tr>
<tr>
<td>Malay (Malaysia)</td>
<td>ms_MY</td>
</tr>
<tr>
<td>Malay</td>
<td>ms</td>
</tr>
<tr>
<td>Maltese (Malta)</td>
<td>mt_MT</td>
</tr>
<tr>
<td>Maltese</td>
<td>mt</td>
</tr>
<tr>
<td>Norwegian (Norway)</td>
<td>no_NO</td>
</tr>
<tr>
<td>Norwegian (Norway,Nynorsk)</td>
<td>no_NO_NY</td>
</tr>
<tr>
<td>Norwegian</td>
<td>no</td>
</tr>
<tr>
<td>Polish (Poland)</td>
<td>pl_PL</td>
</tr>
<tr>
<td>Polish</td>
<td>pl</td>
</tr>
<tr>
<td>Portuguese (Brazil)</td>
<td>pt_BR</td>
</tr>
<tr>
<td>Portuguese (Portugal)</td>
<td>pt_PT</td>
</tr>
<tr>
<td>Portuguese</td>
<td>pt</td>
</tr>
<tr>
<td>Romanian (Romania)</td>
<td>ro_RO</td>
</tr>
<tr>
<td>Romanian</td>
<td>ro</td>
</tr>
<tr>
<td>Russian (Russia)</td>
<td>ru_RU</td>
</tr>
<tr>
<td>Russian</td>
<td>ru</td>
</tr>
<tr>
<td>Serbian (Bosnia and Herzegovina)</td>
<td>sr_BA</td>
</tr>
<tr>
<td>Serbian (Montenegro)</td>
<td>sr_ME</td>
</tr>
<tr>
<td>Serbian (Serbia and Montenegro)</td>
<td>sr_CS</td>
</tr>
<tr>
<td>Serbian (Serbia)</td>
<td>sr_RS</td>
</tr>
<tr>
<td>Serbian</td>
<td>sr</td>
</tr>
<tr>
<td>Slovak (Slovakia)</td>
<td>sk_SK</td>
</tr>
<tr>
<td>Slovak</td>
<td>sk</td>
</tr>
<tr>
<td>Slovenian (Slovenia)</td>
<td>sl_SI</td>
</tr>
<tr>
<td>Slovenian</td>
<td>sl</td>
</tr>
<tr>
<td>Spanish (Argentina)</td>
<td>es_AR</td>
</tr>
<tr>
<td>Spanish (Bolivia)</td>
<td>es_BO</td>
</tr>
<tr>
<td>Spanish (Chile)</td>
<td>es_CL</td>
</tr>
<tr>
<td>Spanish (Colombia)</td>
<td>es_CO</td>
</tr>
<tr>
<td>Spanish (Costa Rica)</td>
<td>es_CR</td>
</tr>
<tr>
<td>Spanish (Dominican Republic)</td>
<td>es_DO</td>
</tr>
<tr>
<td>Spanish (Ecuador)</td>
<td>es_EC</td>
</tr>
<tr>
<td>Spanish (El Salvador)</td>
<td>es_SV</td>
</tr>
<tr>
<td>Spanish (Guatemala)</td>
<td>es_GT</td>
</tr>
<tr>
<td>Spanish (Honduras)</td>
<td>es_HN</td>
</tr>
<tr>
<td>Spanish (Mexico)</td>
<td>es_MX</td>
</tr>
<tr>
<td>Spanish (Nicaragua)</td>
<td>es_NI</td>
</tr>
<tr>
<td>Spanish (Panama)</td>
<td>es_PA</td>
</tr>
<tr>
<td>Spanish (Paraguay)</td>
<td>es_PY</td>
</tr>
<tr>
<td>Spanish (Peru)</td>
<td>es_PE</td>
</tr>
<tr>
<td>Spanish (Puerto Rico)</td>
<td>es_PR</td>
</tr>
<tr>
<td>Spanish (Spain)</td>
<td>es_ES</td>
</tr>
<tr>
<td>Spanish (United States)</td>
<td>es_US</td>
</tr>
<tr>
<td>Spanish (Uruguay)</td>
<td>es_UY</td>
</tr>
<tr>
<td>Spanish (Venezuela)</td>
<td>es_VE</td>
</tr>
<tr>
<td>Spanish</td>
<td>es</td>
</tr>
<tr>
<td>Swedish (Sweden)</td>
<td>sv_SE</td>
</tr>
<tr>
<td>Swedish</td>
<td>sv</td>
</tr>
<tr>
<td>Thai (Thailand)</td>
<td>th_TH</td>
</tr>
<tr>
<td>Thai (Thailand,TH)</td>
<td>th_TH_TH</td>
</tr>
<tr>
<td>Thai</td>
<td>th</td>
</tr>
<tr>
<td>Turkish (Turkey)</td>
<td>tr_TR</td>
</tr>
<tr>
<td>Turkish</td>
<td>tr</td>
</tr>
<tr>
<td>Ukrainian (Ukraine)</td>
<td>uk_UA</td>
</tr>
<tr>
<td>Ukrainian</td>
<td>uk</td>
</tr>
<tr>
<td>Vietnamese (Vietnam)</td>
<td>vi_VN</td>
</tr>
<tr>
<td>Vietnamese</td>
<td>vi</td>
</tr>
</tbody>
</table>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/deploy/static-hosting/</id>
    <title>Static hosting</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/deploy/static-hosting/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Static hosting</h1>
<h2 id="surge">Surge</h2>
<blockquote>
<p>Shipping web projects should be fast, easy, and low risk. Surge is static web publishing for Front-End Developers, right from the CLI.</p>
</blockquote>
<p>➡️ <a href="https://surge.sh" target="_blank" rel="noopener noreferrer">https://surge.sh</a></p>
<p>Terminal:</p>
<pre><code class="language-bash hljs bash" translate="no">npm install -g surge
surge _site/</code></pre>
<p><a href="https://surge.sh/help/" target="_blank" rel="noopener noreferrer">Official documentation</a></p>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/commands/build/</id>
    <title>build</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/commands/build/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>build</h1>
<p>Builds the site.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">Description:
  Builds the website

Usage:
  build [options] [--] [&lt;path&gt;]

Arguments:
  path                               Use the given path as working directory

Options:
  -d, --drafts                       Include drafts
  -u, --baseurl=BASEURL              Set the base URL
  -o, --output=OUTPUT                Set the output directory
      --optimize|--no-optimize       Enable (or disable --no-optimize) optimization of generated files
      --dry-run                      Build without saving
  -c, --config=CONFIG                Set the path to extra config files (comma-separated)
      --clear-cache[=CLEAR-CACHE]    Clear cache before build (optional cache key as regular expression) [default: false]
  -p, --page=PAGE                    Build a specific page
      --render-subset=RENDER-SUBSET  Render a subset of pages
      --show-pages                   Show list of built pages in a table
  -m, --metrics                      Show build metrics (duration and memory) of each step
      --notify                       Send desktop notification on build completion
  -h, --help                         Display help for the given command. When no command is given display help for the list command
  -q, --quiet                        Do not output any message
  -V, --version                      Display this application version
      --ansi|--no-ansi               Force (or disable --no-ansi) ANSI output
  -n, --no-interaction               Do not ask any interactive question
  -v|vv|vvv, --verbose               Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  The build command generates the website in the output directory.

    cecil.phar build
    cecil.phar build path/to/the/working/directory
    cecil.phar build --baseurl=https://example.com/
    cecil.phar build --output=_site

  To build the website with optimization of generated files, you can use the --optimize option.
  This is useful to reduce the size of the generated files and improve performance:

    cecil.phar build --optimize
    cecil.phar build --no-optimize

  To build the website without overwriting files in the output directory, you can use the --dry-run option.
  This is useful to check what would be built without actually writing files:

    cecil.phar build --dry-run

  To build the website with a specific subset of rendered pages, you can use the --render-subset option.
  This is useful to build only a part of the website, for example, only "hot" pages or a specific section:

    cecil.phar build --render-subset=subset

  To show build steps metrics, run:

    cecil.phar build --metrics</code></pre>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/getting-started/starter-kits/</id>
    <title>Starter kits</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/getting-started/starter-kits/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>Starter kits</h1>
<p>To get started quickly, use one of Cecil's ready-to-use <a href="/starter-kits/">starter kits</a>:</p>
<ul>
<li><a href="https://github.com/Cecilapp/the-butler#readme" target="_blank" rel="noopener noreferrer">The Butler</a>: a publishing-ready starter blog.</li>
<li><a href="https://github.com/Cecilapp/Links#readme" target="_blank" rel="noopener noreferrer">Links</a>: an open source Linktree alternative.</li>
<li><a href="https://github.com/Cecilapp/photo-stream#readme" target="_blank" rel="noopener noreferrer">Photo Stream</a>: a super simple self-hosted photo stream.</li>
<li><a href="https://github.com/Cecilapp/statidocs#readme" target="_blank" rel="noopener noreferrer">Statidocs</a>: build a documentation website quickly.</li>
</ul>
<p><a href="https://github.com/Cecilapp/the-butler#readme" target="_blank" rel="noopener noreferrer"><picture>
<source type="image/avif" srcset="/thumbnails/480x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.avif 480w, /thumbnails/768x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.avif 768w, /docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.avif 1014w" width="1014" height="713" sizes="100vw">
<source type="image/webp" srcset="/thumbnails/480x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.webp 480w, /thumbnails/768x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.webp 768w, /docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.webp 1014w" width="1014" height="713" sizes="100vw">
<img src="/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.png" alt="Starter blog example" loading="lazy" decoding="async" class="dark:brightness-90" width="1014" height="713" style=";max-width:100%;height:auto;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAyCAYAAACqNX6+AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAOtUlEQVR4nNWc63rjNg5AD0A5bb92v2nfoO//gptYIrA/AJKQ7Ex3urOtywxDWbFuPMSV1Mjvv//u/IVFVbndbvz444/8/PPP/Otf/+LLr7/y22+/8duvv/Jrtl++fOHLly/88ssv/PTTT7y9vdFao7WN1hQRRVUAQQREBBEByrYIEjvOLZL/oh3ngLq9zjX/sjYZm7XzvOxwfO5wB3fD3XEzzB0zm9Ut/wbo9+jk71L8cVy4+6nm3vzq2OfzcC/n8NiRfXJpT7/rOR9v5XzOsp+vwbhsn640b3ZWL/u3h174O4pzuqnTgxYg5o44CI65oMRIDDmZp5qf/dNWkHmVIQmOe/5VZII4bz8dN/UxLlDqYFlw/VIrmNcAMooXKBcQAcMRNzBB1bJjBXFAfG5L7Ij2pK5SVQmISx5zhsVXt6fCu9725RkGlSqZPqFUaX9NCUkSU6GUG5961wwxA1FcDDdN2+HZ0bK2yU4fNmLaFJ3ARCpMcn/cjTj4pODLjgxJmb9iY477B5U1nsfPUBjP98ISMsfMuHGYkjGMIGYBAzAdxnt1/AMMWMZdzsZ/wpQBRUICBx1nyYMMEINYueFqHy77/BmUWfPvryghY4RVWzdAdDO6GTq8EekYROdNT2oAgdltMmCAiD4BImiqNUnpKkcXKXjmZV3crYtduUrMSQNMyVgD72UkpHoq6yYDRrUdPdWVmqWaESRd3tFLU91ULBOAJxRHKxBNtSVSjj+fp+48Sci3PGTdSM/QrUgMryYh5V5Phtycbs5hDuZod8BCo4TOeuisNYizk0UiZklJ0JQWFUFtSc06VgqUSylSWPd9XooKLYZchst+Go0vICG1VNfQCCnpHjDUHO+GYiH6Ci7p8Fa1IrV/RqAoUzJCOmxuLwmhBI15jmlDqj6S0+VOZerJsySlQh2+RW7Hw8o0JKsTXgJICjGOY05IhsPhzuGhrswcwcK+GLhIHifLI+Ki+2WpJEXO6moCukrIAiH1Bk/xzoN+y3YMABgDQoS89mplnitd+QGIv1tCpqcxXMFqzEM6dgt9q93ABTMwcUwEYyiAS7mM1uHiagEUUNLGrK+vw70C8NmJ8+8P1xowqlcX12wCbWwDTUhA67yS0P9mCSmykVAMpnTs5kh3ejfAcBNMnC4aai1k5hz1Akt3r3aBuXyeaoTp4Q7EMjrMy4imgJO8piwHQyRsm4oiKjQRmiqbCJsKW8LZChQd55Y/kJCaWDt149fyB/9l8Sc1pINpO/Z0dbUb7oYJdFE6zsGCN0CO85YnmADW86zYZbrBFCi+Rq0SmYE1klfnjfPnSWKvRNIT1bRRypb1ppJV2RRMhKYsieFTlfU8e3ryo0d+55J3+l+K49nBy5hP6RDD1OgChziHC0fC6M50Ah7vZej6qpaqKuM84glvaABQdxTLURyfp9TkeRHBMwvgoiAjE91CMppyU+XWlB+a8qbOWxNchVuxf5kwOAOpQVOt8dUVcZ4ysKfczJ8BMaRj1LOre3THMEw7uzgHyu7M2t3pMFXeWXWdsDwAmRAYBpYJQnAahnq2GG2AKUBcQjJWbQGkNZo2bk15a40fNqVbw5qHnLlEDStPkyuQU8C05hueAsmcvpstVfEnoMwO9DOUYUM83d2OcZiwC9zduTvcHXZ3jiElJbD87E7OYNa+AWJKSEpGSxgNY8u2FSCSQEwaljBcGmhDm7E149YaP2zO4Q1zJ5SUI5sSvh9zEkSRBWS6h6oFSEk5IGl8Q2XJgDHyTN8A5dRpfoYy1NBh4f5aNw43dhU+gA8XPsz5cLhbADmGd3Y997zaY0wh+bG6uKGuQD06vrmxecC40dkSjOJoBhaO0qUllIZrQ7QhbWNrxttmHBaeI97i/IRhb3jYD18x0AbPVJXS2gIzdK0DbuFyTmNmHQh39Fug1J47qS2GhICY07uzu3E34R14d3g3eDfnbrBnzNJHQMnVlvhpW8ouKbCGytIE0fCA4cbNjTc6NwkoTRYQQzFpdBomGyYG6mhztuYxsPJ6Oj2tMPBdhD5d4nj27ew365SQWTPVMIi4WIp4GXW5H84zb9/A5ASle/rlBt2cHedDjA+Hf1vU976AHKm6lgt8nhlcUlJGwCmuSK8qDXpzZ0sgNw+VaXRMOl1SbRUJMXEOnC7FNBg0E3p6YqrGdnTeVHhT5TCna0y0eapcBbYFA0RL0k0KEJUiIcQVJw6fascMROyb7ckDFAIKKSn7gOLGhwnv5rx3+Og+7ciRLrOXn8czP1Ndsa1pR9oEYtw8HIqIeDqI4RguAS4kxMMdBw4RLO2KpKpDjdaNrRtvTWMAZY7O/HFQbqdgKaVkQNFiR0bQ5MqMYqvOCTDpAvLtRr56SMZSkd3hAHbCq7qbcTf4SJV1/wTI2cRHPcM4A5rqCmik04Jl7GHT7VWJQNHTADlOFw93HKfndSP/5qg6ew9vcYHw5YhcXMNtBk4TTIGjpSUEQ1zDkJPOQR2VKXZh5J+rr8v1/xCQEaMvDL1PiTk8YOzpbfUCZBx9BRJQ/HKVFX8IQ+2t74aHFR3dcVrauTFAh916VvFQpyO2MvMZ/H7WB1uFMQ11iT9GmqG6ilq+66qoe2RfNSQjFgbIJ5f878sAMqL37pJtSESHHJUBzT+BUOtlCcVsa6Q+YHRqhzPreDSHeG5fanIClWeQWDCe3R7Dy8oOPqUQSlsDqrkrJUc9ok7xqKoBo67W+F/LSZ15gcSq9pXjPz/rasfvoS6rC23D5R/fG4cKJSA9/3BqL1euvsWlbNdvPY4nzxUalyMl3YlLBvU8Z/3/gTIlh9Vx3+casrovxcBZ7VB4TsRiU0qgdL2ni/jn7mojDxWYqZDrMhwf882fXUPmr0v7/WBc2yfS/p2KlN9KKCyZkhKqR+YM4Ix9HBBHdKWUznEOxUbXK53L5u6z604L0kpaBFjzBpfvjhUUz7vt/1e+Hwh5+CSZlwibMsLGMd8fKnLcxZyTGaO1ZIuf1wr8sWyeJ4km0iAi8qCTT6n4Am6tUT3DOUN69bI66QQj9zphF1cm4WwfjJgaGFnfWNA30vWjPs69PCtbTaGPnFTt/CEND0DgtHD4cR3ut0Xtf32R07aQU6yQcx6ChHIKW2ExbnuqI/VhvQKYKbiGOyAS8YeO3BjEPh7V11WEtqvRdXd677NjxQxTncHj+M6AMmCcW14cRi1LLgJKgJHhzLiACW4RS2AxWaYWjnYUxdVBBWmGqCFuU1LWrGCJLD65mxOQusB4fJbM7F5nD+sa1ZmOH6rMvj198teX5XyMnzGaq5Tg+ayWWe3eceuY9TXqREAVadHxqCLa0M1QjzmVk7TwGGKMO5pu7+jAYUMmjOvMYflubZ+/OvCKpXqBSzqGZKisqF2GB5vTAPQD7wfWD+g99BgjJmvIFn6wioI2pD+DkdO1ctFWQ2Vdb3eqqicgrkDq9uuDgMfxOGDItB9LbfmE4d2w3vHjgH2HYwc7wEItoTFlq5aL+ETx1sA2xPoy8u4zU365DYYd+3SRQwXzRx39+iBquUqGsNZNRdZ3wBCPWVHvHT92uO/4fof9jvc95oKcyFi0jZZAmii+NTg26LcJrs7HT3vCickfLwP6Z3X218ozVaUBQWTBkDDmM69lPVTUHjDs4wPfP/Djjvcc/apo2/CeQFSxbcNvHazHaxQWqy4f76Iaevm712X9+XL1GL8+bD6xG1Kkw1ktuczTOt4P/NjxfcfuH9jHO3Z/x/c73o8pIW274R72t22N2+2GHUdIl4XPXFXW4zKk+PyPAPIsKfPMS/n60bEdHaC5inCsJJSy1CciC9xw69APbN+xlI7+8Y59/Ds+9yM6Whvt1nFAtbHdNvrxFjD7kI4RC1QoQ0Lknychz2BUF/VZkrFG2+OBRXSu81VymWca9OYgpKFOF9eOAzt2bP/guAeQ/vEeQI4DcFQbbh0RaK3R325xTD9wSwnJvPsjjLPE/GOAwBnAtQJz0dk5woCakQ4YWgx5iT985K9sqaseQPq+0/cP+j3B3D9CZRFSEWCUvt3o+x4gM2Zxt3SRVzpUquv7T5OQ64iKZTSs2TuIXNLpu2UGdKqqnKIuEjLjgjFyZyDYE8qOHzt23LF9px93+h7V+xHqrW1x/W3DMk6ZMIa6oq4RLpIh52d7eSBTPUnUsRa2ETfvxIP46ftj2T8FxCctlDd3AXy+ZOq9Zz2y7lOFed+nhADQWnxOCPEfBdiSjjJFOJMew9dNGl+NQ16hnGD4WtbfxNkkpAICzvx+AaEX6SAN+pyyZs1rDD0XWWxDrIP3aK1HZN57BoQ9YguzOEyj08cLOPIEQlVVFHU1rjue9WWBjEG0VvqF8d3UuTn0NBxVbUVWdcBYNoMC5ME380gcQiTTLTvT80UhrCM+qs1k4chjVedipNtX8vACI/ViSEidvFqu7+sDmVIBm5Gr/sZSobWCAwYQmepNsiUlxROIIwGCTBoKsVhB1sRwLvFGiXmf4Ra30ZFDzQjzbaymY/nUuAcpg2R0vl8gnOtLAXl2gy6CakhHU2HDuREQlLWKYxp8WW8rzeRoDss1ZZRBuAumMW9jEhiQtSBVc55QJaYaxrnRWAMdoz62W9N4/UCVTeNeW4FSpXdISbVjYwi+DBC5bA/9Gp2QRlzghsyYQ2Wsm12jrkl5ZWx2QPzVKHPjngu0DbqkUZfzypE223CpNU8qqnjT7FjJVw+UrbX5gs4mOtfszncZ+XqFFwIyysktzA+hsoTmQlNny45V97mQOWB4MfzrncIhHZa1eyxW6ASM2Ru57UOFEVBDSmPU0xRpinsBsilta/nfRwWcNqVkZJEf50Dk9NTRvgSQhxuUBWS8vjwCuJYdb0r8JzRCzkHEvPUmzPf4htoamtskYIhANynTmrnQj/EqW0igSW5LBJAuimvWpqBLQlRbWaRe4pzpXHBSTlViankJILUsD4Xlqk49HFOrmrknl2LQZb1vMZwAHe6tLDuDgLukEQ/191V1IqUdgFWIZZplwOjZZq3t8hxIeaYrlPj9H6oxhUTjWngBAAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center;background-size:cover;" srcset="/thumbnails/480x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.png 480w, /thumbnails/768x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.png 768w, /docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.png 1014w" sizes="100vw">
</picture></a></p>]]>
    </content>
  </entry>
  <entry xml:lang="en">
    <id>https://cecil.app/documentation/commands/doctor/</id>
    <title>doctor</title>
    <published>2020-12-19T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <link href="https://cecil.app/documentation/commands/doctor/" rel="alternate" type="text/html" />
    <content type="html">
      <![CDATA[<h1>doctor</h1>
<p>Diagnoses the current site and Cecil environment.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">Description:
  Diagnoses the site configuration

Usage:
  doctor [options] [--] [&lt;path&gt;]

Arguments:
  path                       Use the given path as working directory

Options:
  -c, --config=CONFIG        Set the path to an extra configuration file
  -h, --help                 Display help for the given command. When no command is given display help for the list command
  -q, --quiet                Do not output any message
  -V, --version              Display this application version
      --ansi|--no-ansi       Force (or disable --no-ansi) ANSI output
  -n, --no-interaction       Do not ask any interactive question
  -v|vv|vvv, --verbose       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  The doctor command diagnoses the current site and Cecil environment.

    cecil.phar doctor
    cecil.phar doctor path/to/the/working/directory

  To inspect a site with an extra configuration file, run:

    cecil.phar doctor --config=config.yml</code></pre>
<h2 id="doctor-frontmatter">doctor:frontmatter</h2>
<p>Validates pages front matter syntax.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">Description:
  Validates pages front matter syntax

Usage:
  doctor:frontmatter|doctor:fm [options] [--] [&lt;path&gt;]

Arguments:
  path                  Use the given path as working directory

Options:
  -c, --config=CONFIG   Set the path to an extra configuration file
  -p, --page=PAGE       Validate a single page relative to the pages directory
      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -h, --help            Display help for the given command. When no command is given display help for the list command
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
  -v|vv|vvv             Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</code></pre>
<h2 id="doctor-seo">doctor:seo</h2>
<p>Audits rendered HTML pages for common SEO issues.</p>
<pre><code class="language-plaintext hljs plaintext" translate="no">Description:
  Audits rendered HTML pages for common SEO issues

Usage:
  doctor:seo [options] [--] [&lt;path&gt;]

Arguments:
  path                  Use the given path as working directory

Options:
  -c, --config=CONFIG   Set the path to an extra configuration file
  -p, --page=PAGE       Audit a single page relative to the pages directory
      --format=FORMAT   Output format: text (default) or json
      --feedback        Include findings with feedback level
      --include-virtual Include virtual pages (paginated, taxonomies) in audit</code></pre>
<p>The command builds the site in dry-run mode, then audits the rendered HTML output for a focused set of checks: title tag, meta description, canonical URL, heading structure, Open Graph tags, image alt attributes and estimated content length.</p>
<p>By default, virtual pages (paginated, taxonomy pages) are excluded from the audit. Use <code translate="no">--include-virtual</code> to include them.</p>
<p>By default, findings with level <code translate="no">feedback</code> are not listed.</p>
<p>Use <code translate="no">--feedback</code> to include findings with level <code translate="no">feedback</code> in addition to other findings.</p>
<p>Output results as JSON for CI integration using <code translate="no">--format=json</code>.</p>
<h3 id="configuration">Configuration</h3>
<p>Customize audit thresholds and enabled checks in your configuration file:</p>
<pre><code class="language-yaml hljs yaml" translate="no"><span class="hljs-attr">doctor:</span>
  <span class="hljs-attr">seo:</span>
    <span class="hljs-attr">title:</span> <span class="hljs-string">{</span> <span class="hljs-attr">min:</span> <span class="hljs-number">30</span><span class="hljs-string">,</span> <span class="hljs-attr">max:</span> <span class="hljs-number">60</span> <span class="hljs-string">}</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">{</span> <span class="hljs-attr">min:</span> <span class="hljs-number">120</span><span class="hljs-string">,</span> <span class="hljs-attr">max:</span> <span class="hljs-number">160</span> <span class="hljs-string">}</span>
    <span class="hljs-attr">content:</span> <span class="hljs-string">{</span> <span class="hljs-attr">min_words:</span> <span class="hljs-number">300</span> <span class="hljs-string">}</span>
    <span class="hljs-attr">checks:</span>
      <span class="hljs-attr">title:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">description:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">canonical:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">h1:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">og_tags:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">img_alt:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">content_length:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">lang_attribute:</span> <span class="hljs-literal">true</span></code></pre>]]>
    </content>
  </entry>
</feed>
