{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Cecil - How to?",
  "home_page_url": "https://cecil-945.pages.dev/how-to/",
  "feed_url": "https://cecil-945.pages.dev/how-to/feed.json",
  "description": "Articles and tutorials for common Cecil tasks.",
  "icon": "https://cecil-945.pages.dev/favicon.7ff4b89eec6dad139a7d2d6561543285.png",
  "favicon": "https://cecil-945.pages.dev/thumbnails/64x/favicon.7ff4b89eec6dad139a7d2d6561543285.png",
  "language": "en",
  "items": [
    {
      "id": "https://cecil-945.pages.dev/how-to/markdown-toc-notes/",
      "url": "https://cecil-945.pages.dev/how-to/markdown-toc-notes/",
      "title": "Add a table of contents and notes",
      "summary": "Enrich Cecil Markdown pages with a table of contents, note blocks and custom attributes.",
      "date_published": "2026-10-14T00:00:00+00:00","content_text": "Cecil extends Markdown with a few handy features for long pages: an automatic table of contents, note blocks and attributes on elements.\nInsert a table of contents\nAdd the [toc] tag where the table of contents should appear in the body of the page:\n[toc]\n\n## Installation\n### Requirements\n## Usage\nBy default, the table of contents is built from H2 and H3 headings. Change it in cecil.yml:\npages:\n  body:\n    toc: [h2, h3, h4]\nDisplay the table of contents in a template\nTo place the table of contents outside the content (e.g. in a sidebar), use the toc filter on the page body:\n&lt;aside&gt;\n  {{ page.body|toc }}\n&lt;\/aside&gt;\n&lt;article&gt;\n  {{ page.content }}\n&lt;\/article&gt;\nThe filter also accepts the headings to extract, e.g. {{ page.body|toc(selectors=['h2']) }}.\nAdd notes\nHighlight information with a note block:\n:::tip\n**Tip:** This is advice.\n:::\nIs converted to:\n&lt;aside class=\"note note-tip\"&gt;\n  &lt;p&gt;\n    &lt;strong&gt;Tip:&lt;\/strong&gt; This is advice.\n  &lt;\/p&gt;\n&lt;\/aside&gt;\nAvailable types are info, tip, important, warning and caution (or no type). Style them with the note and note-&lt;type&gt; CSS classes.\nAdd attributes\nSet an id, a class or any attribute on a heading, a fenced code block, a link or an image, inside curly brackets at the end of the line:\n## Installation {#install .highlighted}\nThe #install id gives a stable anchor to link to: [See installation](#install).\nFor an inline element, like a link, add a line break after the closing brace.\nSee the documentation of the table of contents, notes, attributes and the toc filter.",
      "content_html": "<p>Cecil extends Markdown with a few handy features for long pages: an automatic table of contents, note blocks and attributes on elements.</p>\n<h2 id=\"insert-a-table-of-contents\">Insert a table of contents</h2>\n<p>Add the <code translate=\"no\">[toc]</code> tag where the table of contents should appear in the body of the page:</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">[toc]\n\n<span class=\"hljs-section\">## Installation</span>\n<span class=\"hljs-section\">### Requirements</span>\n<span class=\"hljs-section\">## Usage</span></code></pre>\n<p>By default, the table of contents is built from H2 and H3 headings. Change it in <code translate=\"no\">cecil.yml</code>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">body:</span>\n    <span class=\"hljs-attr\">toc:</span> <span class=\"hljs-string\">[h2,</span> <span class=\"hljs-string\">h3,</span> <span class=\"hljs-string\">h4]</span></code></pre>\n<h2 id=\"display-the-table-of-contents-in-a-template\">Display the table of contents in a template</h2>\n<p>To place the table of contents outside the content (e.g. in a sidebar), use the <code translate=\"no\">toc</code> filter on the page body:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">aside</span>&gt;</span>\n  </span><span class=\"hljs-template-variable\">{{ page.body|toc }}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">aside</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">article</span>&gt;</span>\n  </span><span class=\"hljs-template-variable\">{{ page.content }}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">article</span>&gt;</span></span></code></pre>\n<p>The filter also accepts the headings to extract, e.g. <code translate=\"no\">{{ page.body|toc(selectors=['h2']) }}</code>.</p>\n<h2 id=\"add-notes\">Add notes</h2>\n<p>Highlight information with a note block:</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">:::tip\n<span class=\"hljs-strong\">**Tip:**</span> This is advice.\n:::</code></pre>\n<p>Is converted to:</p>\n<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>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p</span>&gt;</span>\n    <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.\n  <span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">p</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">aside</span>&gt;</span></code></pre>\n<p>Available types are <code translate=\"no\">info</code>, <code translate=\"no\">tip</code>, <code translate=\"no\">important</code>, <code translate=\"no\">warning</code> and <code translate=\"no\">caution</code> (or no type). Style them with the <code translate=\"no\">note</code> and <code translate=\"no\">note-&lt;type&gt;</code> CSS classes.</p>\n<h2 id=\"add-attributes\">Add attributes</h2>\n<p>Set an id, a class or any attribute on a heading, a fenced code block, a link or an image, inside curly brackets at the end of the line:</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\"><span class=\"hljs-section\">## Installation {#install .highlighted}</span></code></pre>\n<p>The <code translate=\"no\">#install</code> id gives a stable anchor to link to: <code translate=\"no\">[See installation](#install)</code>.</p>\n<aside class=\"note note-warning\"><p>For an inline element, like a link, add a line break after the closing brace.</p></aside>\n<aside class=\"note note-info\"><p>See the documentation of the <a href=\"/documentation/content/#table-of-contents\">table of contents</a>, <a href=\"/documentation/content/#notes\">notes</a>, <a href=\"/documentation/content/#attributes\">attributes</a> and the <a href=\"/documentation/templates/#toc\"><code translate=\"no\">toc</code> filter</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/content-redirect-alias/",
      "url": "https://cecil-945.pages.dev/how-to/content-redirect-alias/",
      "title": "Redirect old URLs",
      "summary": "Keep old links working in Cecil with the redirect and alias front matter variables.",
      "date_published": "2026-10-13T00:00:00+00:00","content_text": "When a page moves or is renamed, its old URL should not lead to a 404 error. Cecil provides two front matter variables to handle this: alias and redirect.\nRedirect old URLs to a page\nThe alias variable lists the old paths of the current page. Each of them redirects to the page:\n---\ntitle: About\nalias:\n  - contact\n  - about-us\n---\nIn this example, \/contact\/ and \/about-us\/ redirect to \/about\/.\nRedirect a page to another URL\nThe redirect variable turns a page into a redirection to the given URL, internal or external:\n---\ntitle: Old documentation\nredirect: \"https:\/\/cecil.app\/documentation\/\"\n---\nWithout a Markdown file\nIf the old page has no content anymore, declare it as a virtual page in cecil.yml:\npages:\n  virtual:\n    - path: code\n      redirect: https:\/\/github.com\/Cecilapp\/Cecil\nGenerated pages\nFor each alias and each redirection, Cecil renders a page with the built-in redirect.html.twig template. It contains:\n\na &lt;link rel=\"canonical\"&gt; to the target URL\na &lt;meta name=\"robots\" content=\"noindex\"&gt; tag, so search engines don’t index it\na &lt;meta http-equiv=\"refresh\"&gt; tag and a JavaScript redirection\na fallback link for visitors\n\nTo customize this page, create your own layouts\/redirect.html.twig template: it takes precedence over the built-in one.\nSee the documentation of the redirect and alias variables.",
      "content_html": "<p>When a page moves or is renamed, its old URL should not lead to a 404 error. Cecil provides two front matter variables to handle this: <code translate=\"no\">alias</code> and <code translate=\"no\">redirect</code>.</p>\n<h2 id=\"redirect-old-urls-to-a-page\">Redirect old URLs to a page</h2>\n<p>The <code translate=\"no\">alias</code> variable lists the old paths of the current page. Each of them redirects to the page:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">About</span>\n<span class=\"hljs-attr\">alias:</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">contact</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">about-us</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<p>In this example, <code translate=\"no\">/contact/</code> and <code translate=\"no\">/about-us/</code> redirect to <code translate=\"no\">/about/</code>.</p>\n<h2 id=\"redirect-a-page-to-another-url\">Redirect a page to another URL</h2>\n<p>The <code translate=\"no\">redirect</code> variable turns a page into a redirection to the given URL, internal or external:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">Old</span> <span class=\"hljs-string\">documentation</span>\n<span class=\"hljs-attr\">redirect:</span> <span class=\"hljs-string\">\"https://cecil.app/documentation/\"</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<h2 id=\"without-a-markdown-file\">Without a Markdown file</h2>\n<p>If the old page has no content anymore, declare it as a <a href=\"/documentation/configuration/#pages-virtual\">virtual page</a> in <code translate=\"no\">cecil.yml</code>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">virtual:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">code</span>\n      <span class=\"hljs-attr\">redirect:</span> <span class=\"hljs-string\">https://github.com/Cecilapp/Cecil</span></code></pre>\n<h2 id=\"generated-pages\">Generated pages</h2>\n<p>For each alias and each redirection, Cecil renders a page with the built-in <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. It contains:</p>\n<ul>\n<li>a <code translate=\"no\">&lt;link rel=\"canonical\"&gt;</code> to the target URL</li>\n<li>a <code translate=\"no\">&lt;meta name=\"robots\" content=\"noindex\"&gt;</code> tag, so search engines don’t index it</li>\n<li>a <code translate=\"no\">&lt;meta http-equiv=\"refresh\"&gt;</code> tag and a JavaScript redirection</li>\n<li>a fallback link for visitors</li>\n</ul>\n<aside class=\"note note-tip\"><p>To customize this page, create your own <code translate=\"no\">layouts/redirect.html.twig</code> template: it takes precedence over the built-in one.</p></aside>\n<aside class=\"note note-info\"><p>See the documentation of the <a href=\"/documentation/content/#redirect\"><code translate=\"no\">redirect</code></a> and <a href=\"/documentation/content/#alias\"><code translate=\"no\">alias</code></a> variables.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/config-environments/",
      "url": "https://cecil-945.pages.dev/how-to/config-environments/",
      "title": "Use different settings per environment",
      "summary": "Override the Cecil configuration per environment with CECIL_ environment variables and extra config files.",
      "date_published": "2026-10-12T00:00:00+00:00","content_text": "Your local site and your production site rarely share the same settings: base URL, debug mode, output directory… Cecil lets you keep a single cecil.yml and override it per environment.\nKeep production values in cecil.yml\ntitle: \"My site\"\nbaseurl: https:\/\/example.com\/\ndebug: false\nOverride with extra config files\nCreate a file with only the keys to change, for example config\/dev.yml:\nbaseurl: http:\/\/localhost:8000\/\ndebug: true\nThen load it with the --config option, available with the build and serve commands:\nphp cecil.phar serve --config=config\/dev.yml\nYou can pass several files, separated by commas. They are merged in order after cecil.yml, so each file overrides the previous ones:\nphp cecil.phar build --config=config\/prod.yml,config\/local.yml\nOverride with environment variables\nAny configuration key can be set with an environment variable prefixed with CECIL_, in uppercase. Nested keys are separated with an underscore (e.g. CECIL_OUTPUT_DIR sets output.dir):\nexport CECIL_BASEURL=\"https:\/\/staging.example.com\/\"\nexport CECIL_DEBUG=true\nphp cecil.phar build\nCecil also loads a .env file from the site root, if it exists (variables already defined by the system are preserved):\nCECIL_BASEURL=\"http:\/\/localhost:8000\/\"\nCECIL_DEBUG=true\nEnvironment variables are applied on top of the configuration files, which makes them handy in CI, for example:\n# .gitlab-ci.yml\npages:\n  variables:\n    CECIL_ENV: production\n    CECIL_OUTPUT_DIR: public\nRead the environment in templates\nUse the getenv function to adapt templates, for example to include analytics in production only:\n{% if getenv('CECIL_ENV') == 'production' %}\n  {{ include('partials\/analytics.html.twig') }}\n{% endif %}\nThe site.debug variable tells you if debug mode is enabled:\n{% if site.debug %}{{ dump(page) }}{% endif %}\nSee the documentation for overriding the configuration, the debug option and the getenv function.",
      "content_html": "<p>Your local site and your production site rarely share the same settings: base URL, debug mode, output directory… Cecil lets you keep a single <code translate=\"no\">cecil.yml</code> and override it per environment.</p>\n<h2 id=\"keep-production-values-in-cecil-yml\">Keep production values in <code translate=\"no\">cecil.yml</code></h2>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">\"My site\"</span>\n<span class=\"hljs-attr\">baseurl:</span> <span class=\"hljs-string\">https://example.com/</span>\n<span class=\"hljs-attr\">debug:</span> <span class=\"hljs-literal\">false</span></code></pre>\n<h2 id=\"override-with-extra-config-files\">Override with extra config files</h2>\n<p>Create a file with only the keys to change, for example <code translate=\"no\">config/dev.yml</code>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">baseurl:</span> <span class=\"hljs-string\">http://localhost:8000/</span>\n<span class=\"hljs-attr\">debug:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<p>Then load it with the <code translate=\"no\">--config</code> option, available with the <code translate=\"no\">build</code> and <code translate=\"no\">serve</code> commands:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar serve --config=config/dev.yml</code></pre>\n<p>You can pass several files, separated by commas. They are merged in order after <code translate=\"no\">cecil.yml</code>, so each file overrides the previous ones:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar build --config=config/prod.yml,config/local.yml</code></pre>\n<h2 id=\"override-with-environment-variables\">Override with environment variables</h2>\n<p>Any configuration key can be set with an environment variable prefixed with <code translate=\"no\">CECIL_</code>, in uppercase. Nested keys are separated with an underscore (e.g. <code translate=\"no\">CECIL_OUTPUT_DIR</code> sets <code translate=\"no\">output.dir</code>):</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\"><span class=\"hljs-built_in\">export</span> CECIL_BASEURL=<span class=\"hljs-string\">\"https://staging.example.com/\"</span>\n<span class=\"hljs-built_in\">export</span> CECIL_DEBUG=<span class=\"hljs-literal\">true</span>\nphp cecil.phar build</code></pre>\n<p>Cecil also loads a <code translate=\"no\">.env</code> file from the site root, if it exists (variables already defined by the system are preserved):</p>\n<pre><code class=\"language-dotenv\" translate=\"no\">CECIL_BASEURL=\"http://localhost:8000/\"\nCECIL_DEBUG=true</code></pre>\n<p>Environment variables are applied on top of the configuration files, which makes them handy in CI, for example:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-comment\"># .gitlab-ci.yml</span>\n<span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">variables:</span>\n    <span class=\"hljs-attr\">CECIL_ENV:</span> <span class=\"hljs-string\">production</span>\n    <span class=\"hljs-attr\">CECIL_OUTPUT_DIR:</span> <span class=\"hljs-string\">public</span></code></pre>\n<h2 id=\"read-the-environment-in-templates\">Read the environment in templates</h2>\n<p>Use the <code translate=\"no\">getenv</code> function to adapt templates, for example to include analytics in production only:</p>\n<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> getenv('CECIL_ENV') == 'production' %}</span><span class=\"xml\">\n  </span><span class=\"hljs-template-variable\">{{ <span class=\"hljs-name\">include</span><span class=\"hljs-params\">('partials/analytics.html.twig')</span> }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endif</span></span> %}</span></code></pre>\n<p>The <code translate=\"no\">site.debug</code> variable tells you if debug mode is enabled:</p>\n<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> site.debug %}</span><span class=\"hljs-template-variable\">{{ <span class=\"hljs-name\">dump</span><span class=\"hljs-params\">(page)</span> }}</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endif</span></span> %}</span></code></pre>\n<aside class=\"note note-info\"><p>See the documentation for <a href=\"/documentation/configuration/#override-configuration\">overriding the configuration</a>, the <a href=\"/documentation/configuration/#debug\"><code translate=\"no\">debug</code> option</a> and the <a href=\"/documentation/templates/#getenv\"><code translate=\"no\">getenv</code> function</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/templates-assets-scss/",
      "url": "https://cecil-945.pages.dev/how-to/templates-assets-scss/",
      "title": "Compile, minify and fingerprint CSS and JavaScript",
      "summary": "Process Sass, CSS and JavaScript files in Cecil templates with the asset function and its filters.",
      "date_published": "2026-10-11T00:00:00+00:00","content_text": "Cecil can compile Sass files, minify CSS and JavaScript, and add a content hash to file names (cache busting) directly from your templates, without any external build tool.\nConfigure assets processing\nSource files are stored in the assets\/ directory. Processing is enabled by default and can be tuned in cecil.yml:\nassets:\n  compile:\n    style: compressed    # `expanded` or `compressed`\n    import: [sass, scss] # imported paths\n  minify: true           # minifies CSS and JS\n  fingerprint: true      # adds content hash to file names\nLoad a stylesheet\nCreate an asset from a Sass file, compile it with to_css and render the &lt;link&gt; element with the html function:\n{{ html(asset('css\/styles.scss')|to_css|minify) }}\nFilters can be chained to control each step explicitly, which is useful if a feature is disabled globally:\n{{ asset('css\/styles.scss')|to_css|minify|fingerprint }}\nBundle JavaScript files\nPass an array of paths to create a single bundle, with a custom file name:\n{{ html(asset(['js\/menu.js', 'js\/search.js'], {filename: 'scripts.js'})) }}\nOptions can also be set per asset, for example to keep a remote file untouched:\n{{ html(asset('https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/anchor-js\/4.3.1\/anchor.min.js', {minify: false})) }}\nAdd Subresource Integrity\nUse the integrity function (or the integrity attribute of the asset) to generate a sha384 hash:\n{% set styles = asset('css\/styles.scss')|to_css %}\n&lt;link rel=\"stylesheet\" href=\"{{ url(styles) }}\" integrity=\"{{ integrity(styles) }}\" crossorigin=\"anonymous\"&gt;\nInline critical CSS\nOutput the content of an asset with the inline filter:\n&lt;style&gt;{{ asset('css\/critical.scss')|to_css|minify|inline }}&lt;\/style&gt;\nFor a few lines of styles written directly in the template, use the scss_to_css and minify_css filters:\n&lt;style&gt;\n{% apply scss_to_css|minify_css %}\n  $color: #fcfcfc;\n  body {\n    background-color: $color;\n  }\n{% endapply %}\n&lt;\/style&gt;\nSee the documentation of the asset function, the filters and the assets configuration.",
      "content_html": "<p>Cecil can compile <a href=\"https://sass-lang.com\" target=\"_blank\" rel=\"noopener noreferrer\">Sass</a> files, minify CSS and JavaScript, and add a content hash to file names (cache busting) directly from your templates, without any external build tool.</p>\n<h2 id=\"configure-assets-processing\">Configure assets processing</h2>\n<p>Source files are stored in the <code translate=\"no\">assets/</code> directory. Processing is enabled by default and can be tuned in <code translate=\"no\">cecil.yml</code>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">assets:</span>\n  <span class=\"hljs-attr\">compile:</span>\n    <span class=\"hljs-attr\">style:</span> <span class=\"hljs-string\">compressed</span>    <span class=\"hljs-comment\"># `expanded` or `compressed`</span>\n    <span class=\"hljs-attr\">import:</span> <span class=\"hljs-string\">[sass,</span> <span class=\"hljs-string\">scss]</span> <span class=\"hljs-comment\"># imported paths</span>\n  <span class=\"hljs-attr\">minify:</span> <span class=\"hljs-literal\">true</span>           <span class=\"hljs-comment\"># minifies CSS and JS</span>\n  <span class=\"hljs-attr\">fingerprint:</span> <span class=\"hljs-literal\">true</span>      <span class=\"hljs-comment\"># adds content hash to file names</span></code></pre>\n<h2 id=\"load-a-stylesheet\">Load a stylesheet</h2>\n<p>Create an <em>asset</em> from a Sass file, compile it with <code translate=\"no\">to_css</code> and render the <code translate=\"no\">&lt;link&gt;</code> element with the <code translate=\"no\">html</code> function:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ html(asset('css/styles.scss')|to_css|minify) }}</span></code></pre>\n<p>Filters can be chained to control each step explicitly, which is useful if a feature is disabled globally:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset('css/styles.scss')|to_css|minify|fingerprint }}</span></code></pre>\n<h2 id=\"bundle-javascript-files\">Bundle JavaScript files</h2>\n<p>Pass an array of paths to create a single bundle, with a custom file name:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ html(asset(['js/menu.js', 'js/search.js'], {filename: 'scripts.js'})) }}</span></code></pre>\n<p>Options can also be set per asset, for example to keep a remote file untouched:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ html(asset('https://cdnjs.cloudflare.com/ajax/libs/anchor-js/4.3.1/anchor.<span class=\"hljs-name\">min</span>.js', {minify: false})) }}</span></code></pre>\n<h2 id=\"add-subresource-integrity\">Add Subresource Integrity</h2>\n<p>Use the <code translate=\"no\">integrity</code> function (or the <code translate=\"no\">integrity</code> attribute of the asset) to generate a <code translate=\"no\">sha384</code> hash:</p>\n<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 = asset('css/styles.scss')|to_css %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link</span> <span class=\"hljs-attr\">rel</span>=<span class=\"hljs-string\">\"stylesheet\"</span> <span class=\"hljs-attr\">href</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ url(styles) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">integrity</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ integrity(styles) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">crossorigin</span>=<span class=\"hljs-string\">\"anonymous\"</span>&gt;</span></span></code></pre>\n<h2 id=\"inline-critical-css\">Inline critical CSS</h2>\n<p>Output the content of an asset with the <code translate=\"no\">inline</code> filter:</p>\n<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-variable\">{{ asset('css/critical.scss')|to_css|minify|inline }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">style</span>&gt;</span></span></code></pre>\n<p>For a few lines of styles written directly in the template, use the <code translate=\"no\">scss_to_css</code> and <code translate=\"no\">minify_css</code> filters:</p>\n<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>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">apply</span></span> scss_to_css|minify_css %}</span><span class=\"xml\">\n  $color: #fcfcfc;\n  body {\n    background-color: $color;\n  }\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endapply</span></span> %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">style</span>&gt;</span></span></code></pre>\n<aside class=\"note note-info\"><p>See the documentation of the <a href=\"/documentation/templates/#asset\"><code translate=\"no\">asset</code> function</a>, the <a href=\"/documentation/templates/#filters\">filters</a> and the <a href=\"/documentation/configuration/#assets\">assets configuration</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/content-taxonomies/",
      "url": "https://cecil-945.pages.dev/how-to/content-taxonomies/",
      "title": "Organize pages with tags and categories",
      "summary": "Classify Cecil pages with taxonomies like tags and categories, and customize their list pages.",
      "date_published": "2026-10-10T00:00:00+00:00","content_text": "Taxonomies let you classify pages with terms (e.g. PHP) grouped in vocabularies (e.g. tags). Cecil then generates a page per vocabulary and a page per term.\nDeclare vocabularies\nVocabularies are declared in cecil.yml, paired by plural and singular names:\ntaxonomies:\n  categories: category\n  tags: tag\nThere are no default vocabularies: you must declare them to use them.\nClassify pages\nAdd terms in the front matter of your pages, using the plural name of the vocabulary:\n---\ntitle: My first post\ncategories: [\"Development\"]\ntags: [\"PHP\", \"Static site\"]\n---\nCecil generates:\n\n\/tags\/: the list of the terms of the vocabulary\n\/tags\/php\/ and \/tags\/static-site\/: the list of the pages of each term\n\nCustomize the list of terms\nThe vocabulary template is named after the plural, e.g. layouts\/taxonomy\/tags.html.twig:\n{% extends 'page.html.twig' %}\n\n{% block content %}\n  &lt;h1&gt;{{ page.title }}&lt;\/h1&gt;\n  &lt;ul&gt;\n  {% for term in page.terms %}\n    &lt;li&gt;&lt;a href=\"{{ url(term.id) }}\"&gt;{{ term.name }}&lt;\/a&gt; ({{ term|length }})&lt;\/li&gt;\n  {% endfor %}\n  &lt;\/ul&gt;\n{% endblock %}\nCustomize the pages of a term\nThe term template is named after the singular, e.g. layouts\/taxonomy\/tag.html.twig:\n{% extends 'page.html.twig' %}\n\n{% block content %}\n  &lt;h1&gt;{{ page.title }}&lt;\/h1&gt;\n  {% for p in page.paginator.pages ?? page.pages %}\n    &lt;article&gt;\n      &lt;h2&gt;&lt;a href=\"{{ url(p) }}\"&gt;{{ p.title }}&lt;\/a&gt;&lt;\/h2&gt;\n    &lt;\/article&gt;\n  {% endfor %}\n  &lt;a href=\"{{ url(page.plural) }}\"&gt;All {{ page.plural }}&lt;\/a&gt;\n{% endblock %}\nDisplay the terms of a page\nIn a page template, link each term to its page:\n{% for tag in page.tags ?? [] %}\n  &lt;a href=\"{{ url('tags\/' ~ tag) }}\"&gt;{{ tag }}&lt;\/a&gt;\n{% endfor %}\nOr use the built-in partial: {{ include('partials\/terms-list.html.twig', {vocabulary: 'tags'}) }}.\nSee the documentation of taxonomies configuration, templates lookup rules and taxonomy variables.",
      "content_html": "<p>Taxonomies let you classify pages with terms (e.g. <code translate=\"no\">PHP</code>) grouped in vocabularies (e.g. <code translate=\"no\">tags</code>). Cecil then generates a page per vocabulary and a page per term.</p>\n<h2 id=\"declare-vocabularies\">Declare vocabularies</h2>\n<p>Vocabularies are declared in <code translate=\"no\">cecil.yml</code>, paired by plural and singular names:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">taxonomies:</span>\n  <span class=\"hljs-attr\">categories:</span> <span class=\"hljs-string\">category</span>\n  <span class=\"hljs-attr\">tags:</span> <span class=\"hljs-string\">tag</span></code></pre>\n<aside class=\"note note-warning\"><p>There are no default vocabularies: you must declare them to use them.</p></aside>\n<h2 id=\"classify-pages\">Classify pages</h2>\n<p>Add terms in the front matter of your pages, using the plural name of the vocabulary:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">My</span> <span class=\"hljs-string\">first</span> <span class=\"hljs-string\">post</span>\n<span class=\"hljs-attr\">categories:</span> <span class=\"hljs-string\">[\"Development\"]</span>\n<span class=\"hljs-attr\">tags:</span> <span class=\"hljs-string\">[\"PHP\",</span> <span class=\"hljs-string\">\"Static site\"</span><span class=\"hljs-string\">]</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<p>Cecil generates:</p>\n<ul>\n<li><code translate=\"no\">/tags/</code>: the list of the terms of the vocabulary</li>\n<li><code translate=\"no\">/tags/php/</code> and <code translate=\"no\">/tags/static-site/</code>: the list of the pages of each term</li>\n</ul>\n<h2 id=\"customize-the-list-of-terms\">Customize the list of terms</h2>\n<p>The <em>vocabulary</em> template is named after the <strong>plural</strong>, e.g. <code translate=\"no\">layouts/taxonomy/tags.html.twig</code>:</p>\n<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\">\n\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">block</span></span> content %}</span><span class=\"xml\">\n  <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>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul</span>&gt;</span>\n  </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\">\n    <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>\n  </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span><span class=\"xml\">\n  <span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">ul</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endblock</span></span> %}</span></code></pre>\n<h2 id=\"customize-the-pages-of-a-term\">Customize the pages of a term</h2>\n<p>The <em>term</em> template is named after the <strong>singular</strong>, e.g. <code translate=\"no\">layouts/taxonomy/tag.html.twig</code>:</p>\n<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\">\n\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">block</span></span> content %}</span><span class=\"xml\">\n  <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>\n  </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\">\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">article</span>&gt;</span>\n      <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>\n    <span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">article</span>&gt;</span>\n  </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span><span class=\"xml\">\n  <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>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endblock</span></span> %}</span></code></pre>\n<h2 id=\"display-the-terms-of-a-page\">Display the terms of a page</h2>\n<p>In a page template, link each term to its page:</p>\n<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> tag in page.tags ?? [] %}</span><span class=\"xml\">\n  <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('tags/' ~ tag) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ tag }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span></code></pre>\n<p>Or use the built-in partial: <code translate=\"no\">{{ include('partials/terms-list.html.twig', {vocabulary: 'tags'}) }}</code>.</p>\n<aside class=\"note note-info\"><p>See the documentation of <a href=\"/documentation/configuration/#taxonomies\">taxonomies configuration</a>, <a href=\"/documentation/templates/#type-vocabulary\">templates lookup rules</a> and <a href=\"/documentation/templates/#page\">taxonomy variables</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/templates-menus/",
      "url": "https://cecil-945.pages.dev/how-to/templates-menus/",
      "title": "Build a navigation menu",
      "summary": "Declare menus in Cecil configuration or front matter and render them in templates.",
      "date_published": "2026-10-09T00:00:00+00:00","content_text": "Cecil manages navigation menus as collections of entries (name, URL, weight) that you can declare in the configuration or directly in pages, then render anywhere in your templates.\nDeclare entries in configuration\nAdd a menus section in cecil.yml. Each menu (e.g.: main, footer) is a list of entries with a unique id:\nmenus:\n  main:\n    - id: about\n      name: \"About\"\n      url: \/about\/\n      weight: 1\n  footer:\n    - id: github\n      name: \"GitHub\"\n      url: https:\/\/github.com\/Cecilapp\/Cecil\n      weight: 99\nA main menu is automatically created with the home page entry and all sections entries.\nAdd a page from its front matter\nA page can add itself to one or more menus with the menu variable. The entry name is the page title and the URL is the page path:\n---\ntitle: Our Expertise\nmenu:\n  main:\n    weight: 15\n  footer:\n    weight: 15\n    name: \"Expertise\" # override the entry name in this menu\n---\nOverride or disable an entry\nUse the page ID as id to override an existing entry, or enabled: false to remove it:\nmenus:\n  main:\n    - id: index\n      name: \"Home\"\n      weight: 1\n    - id: about\n      enabled: false\nRender the menu\nLoop over site.menus.&lt;menu&gt;, sorted by weight, and compare each entry ID with the current page ID to highlight the active item:\n&lt;nav&gt;\n  &lt;ul&gt;\n  {% for entry in site.menus.main|sort_by_weight %}\n    &lt;li&gt;\n      &lt;a href=\"{{ url(entry.url) }}\"{% if entry.id == page.id %} aria-current=\"page\" class=\"active\"{% endif %}&gt;{{ entry.name }}&lt;\/a&gt;\n    &lt;\/li&gt;\n  {% endfor %}\n  &lt;\/ul&gt;\n&lt;\/nav&gt;\nA single entry can also be reached by its ID:\n{% if site.menus.main.about is defined %}\n  &lt;a href=\"{{ url(site.menus.main.about.url) }}\"&gt;{{ site.menus.main.about.name }}&lt;\/a&gt;\n{% endif %}\nCecil also provides a built-in partial: {{ include('partials\/navigation.html.twig', {menu: 'main'}) }}.\nSee the documentation of the menus configuration, the menu front matter variable and the site.menus variable.",
      "content_html": "<p>Cecil manages navigation menus as collections of entries (name, URL, weight) that you can declare in the configuration or directly in pages, then render anywhere in your templates.</p>\n<h2 id=\"declare-entries-in-configuration\">Declare entries in configuration</h2>\n<p>Add a <code translate=\"no\">menus</code> section in <code translate=\"no\">cecil.yml</code>. Each menu (e.g.: <code translate=\"no\">main</code>, <code translate=\"no\">footer</code>) is a list of entries with a unique <code translate=\"no\">id</code>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">menus:</span>\n  <span class=\"hljs-attr\">main:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">id:</span> <span class=\"hljs-string\">about</span>\n      <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">\"About\"</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">/about/</span>\n      <span class=\"hljs-attr\">weight:</span> <span class=\"hljs-number\">1</span>\n  <span class=\"hljs-attr\">footer:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">id:</span> <span class=\"hljs-string\">github</span>\n      <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">\"GitHub\"</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">https://github.com/Cecilapp/Cecil</span>\n      <span class=\"hljs-attr\">weight:</span> <span class=\"hljs-number\">99</span></code></pre>\n<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.</p></aside>\n<h2 id=\"add-a-page-from-its-front-matter\">Add a page from its front matter</h2>\n<p>A page can add itself to one or more menus with the <code translate=\"no\">menu</code> variable. The entry name is the page <code translate=\"no\">title</code> and the URL is the page path:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">Our</span> <span class=\"hljs-string\">Expertise</span>\n<span class=\"hljs-attr\">menu:</span>\n  <span class=\"hljs-attr\">main:</span>\n    <span class=\"hljs-attr\">weight:</span> <span class=\"hljs-number\">15</span>\n  <span class=\"hljs-attr\">footer:</span>\n    <span class=\"hljs-attr\">weight:</span> <span class=\"hljs-number\">15</span>\n    <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">\"Expertise\"</span> <span class=\"hljs-comment\"># override the entry name in this menu</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<h2 id=\"override-or-disable-an-entry\">Override or disable an entry</h2>\n<p>Use the page ID as <code translate=\"no\">id</code> to override an existing entry, or <code translate=\"no\">enabled: false</code> to remove it:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">menus:</span>\n  <span class=\"hljs-attr\">main:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">id:</span> <span class=\"hljs-string\">index</span>\n      <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">\"Home\"</span>\n      <span class=\"hljs-attr\">weight:</span> <span class=\"hljs-number\">1</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">id:</span> <span class=\"hljs-string\">about</span>\n      <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">false</span></code></pre>\n<h2 id=\"render-the-menu\">Render the menu</h2>\n<p>Loop over <code translate=\"no\">site.menus.&lt;menu&gt;</code>, sorted by weight, and compare each entry ID with the current page ID to highlight the active item:</p>\n<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>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul</span>&gt;</span>\n  </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\">\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li</span>&gt;</span>\n      <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></span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">if</span></span> entry.id == page.id %}</span><span class=\"xml\"><span class=\"hljs-tag\"> <span class=\"hljs-attr\">aria-current</span>=<span class=\"hljs-string\">\"page\"</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"active\"</span></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\">&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>\n    <span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">li</span>&gt;</span>\n  </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span><span class=\"xml\">\n  <span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">ul</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">nav</span>&gt;</span></span></code></pre>\n<p>A single entry can also be reached by its ID:</p>\n<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> site.menus.main.about is defined %}</span><span class=\"xml\">\n  <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.menus.main.about.url) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ site.menus.main.about.name }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endif</span></span> %}</span></code></pre>\n<aside class=\"note note-tip\"><p>Cecil also provides a built-in partial: <code translate=\"no\">{{ include('partials/navigation.html.twig', {menu: 'main'}) }}</code>.</p></aside>\n<aside class=\"note note-info\"><p>See the documentation of the <a href=\"/documentation/configuration/#menus\">menus configuration</a>, the <a href=\"/documentation/content/#menu\"><code translate=\"no\">menu</code> front matter variable</a> and the <a href=\"/documentation/templates/#site\"><code translate=\"no\">site.menus</code> variable</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/output-search-index/",
      "url": "https://cecil-945.pages.dev/how-to/output-search-index/",
      "title": "Add client-side search",
      "summary": "Add a client-side search to a Cecil static site with a JSON index of pages and a small JavaScript library.",
      "date_published": "2026-10-08T00:00:00+00:00","content_text": "A static site has no server-side search, but Cecil can generate a JSON index of your pages at build time. A few lines of JavaScript are then enough to search it in the browser.\nCreate the index page\nCreate a page whose only output format is json, with a dedicated layout:\npages\/search.md\n---\ntitle: Search index\nlayout: search\noutput: json\nexcluded: true\n---\nexcluded: true keeps this technical page out of list pages and the sitemap.\nWrite the JSON template\nFollowing the lookup rules, Cecil renders this page with layouts\/search.json.twig. Build an array of the showable pages, then encode it:\n{%- set index = [] -%}\n{%- for p in site.pages.showable|filter(p =&gt; p.type == 'page') -%}\n  {%- set index = index|merge([{\n    title: p.title,\n    url: url(p),\n    description: p.description|default(''),\n    content: p.content|striptags|excerpt(300)\n  }]) -%}\n{%- endfor -%}\n{{ index|json_encode(constant('JSON_UNESCAPED_UNICODE') b-or constant('JSON_UNESCAPED_SLASHES'))|raw }}\nAfter php cecil.phar build, the index is available at \/search.json.\nSee the output variable, the output formats and the url function documentation.\nSearch with Fuse.js\nAdd a search field to a template and load Fuse.js from a CDN to query the index:\n&lt;input type=\"search\" id=\"search\" placeholder=\"Search…\"&gt;\n&lt;ul id=\"results\"&gt;&lt;\/ul&gt;\n\n&lt;script type=\"module\"&gt;\n  import Fuse from 'https:\/\/cdn.jsdelivr.net\/npm\/fuse.js@7\/dist\/fuse.mjs';\n\n  const index = await fetch('{{ url('search', {format: 'json'}) }}').then(r =&gt; r.json());\n  const fuse = new Fuse(index, { keys: ['title', 'description', 'content'], threshold: 0.3 });\n  const results = document.getElementById('results');\n\n  document.getElementById('search').addEventListener('input', (e) =&gt; {\n    results.replaceChildren(...fuse.search(e.target.value, { limit: 10 }).map(({ item }) =&gt; {\n      const li = document.createElement('li');\n      const a = document.createElement('a');\n      a.href = item.url;\n      a.textContent = item.title;\n      li.append(a);\n      return li;\n    }));\n  });\n&lt;\/script&gt;\nAlternative: Pagefind\nPagefind indexes the generated HTML files instead: no JSON template is needed. Run it after the build, on the output directory (_site by default):\nphp cecil.phar build\nnpx pagefind --site _site\nThen load its UI in a template:\n&lt;link href=\"\/pagefind\/pagefind-ui.css\" rel=\"stylesheet\"&gt;\n&lt;script src=\"\/pagefind\/pagefind-ui.js\"&gt;&lt;\/script&gt;\n&lt;div id=\"search\"&gt;&lt;\/div&gt;\n&lt;script&gt;\n  window.addEventListener('DOMContentLoaded', () =&gt; new PagefindUI({ element: '#search' }));\n&lt;\/script&gt;\nThe index is a regular page: to regenerate only JSON outputs, declare a subset and run php cecil.phar build --render-subset=&lt;name&gt;.",
      "content_html": "<p>A static site has no server-side search, but Cecil can generate a <strong>JSON index of your pages</strong> at build time. A few lines of JavaScript are then enough to search it in the browser.</p>\n<h2 id=\"create-the-index-page\">Create the index page</h2>\n<p>Create a page whose only <a href=\"/documentation/content/#output\">output format</a> is <code translate=\"no\">json</code>, with a dedicated layout:</p>\n<p><em>pages/search.md</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">Search</span> <span class=\"hljs-string\">index</span>\n<span class=\"hljs-attr\">layout:</span> <span class=\"hljs-string\">search</span>\n<span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">json</span>\n<span class=\"hljs-attr\">excluded:</span> <span class=\"hljs-literal\">true</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<p><a href=\"/documentation/content/#excluded\"><code translate=\"no\">excluded: true</code></a> keeps this technical page out of list pages and the sitemap.</p>\n<h2 id=\"write-the-json-template\">Write the JSON template</h2>\n<p>Following the <a href=\"/documentation/templates/#lookup-rules\">lookup rules</a>, Cecil renders this page with <code translate=\"no\">layouts/search.json.twig</code>. Build an array of the <a href=\"/documentation/templates/#site\">showable</a> pages, then encode it:</p>\n<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> index = [] -%}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{%- <span class=\"hljs-name\"><span class=\"hljs-keyword\">for</span></span> p in site.pages.showable|<span class=\"hljs-keyword\">filter</span>(p =&gt; p.type == 'page') -%}</span><span class=\"xml\">\n  </span><span class=\"hljs-template-tag\">{%- <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> index = index|<span class=\"hljs-keyword\">merge</span>([{\n    title: p.title,\n    url: url(p),\n    description: p.description|<span class=\"hljs-keyword\">default</span>(''),\n    content: p.content|<span class=\"hljs-keyword\">striptags</span>|excerpt(300)\n  }]) -%}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{%- <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> -%}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ index|<span class=\"hljs-keyword\">json_encode</span>(<span class=\"hljs-name\">constant</span><span class=\"hljs-params\">('JSON_UNESCAPED_UNICODE')</span> b-or <span class=\"hljs-name\">constant</span><span class=\"hljs-params\">('JSON_UNESCAPED_SLASHES')</span>)|<span class=\"hljs-keyword\">raw</span> }}</span></code></pre>\n<p>After <code translate=\"no\">php cecil.phar build</code>, the index is available at <code translate=\"no\">/search.json</code>.</p>\n<aside class=\"note note-info\"><p>See the <a href=\"/documentation/content/#output\"><code translate=\"no\">output</code> variable</a>, the <a href=\"/documentation/configuration/#output-formats\">output formats</a> and the <a href=\"/documentation/templates/#url\"><code translate=\"no\">url</code> function</a> documentation.</p></aside>\n<h2 id=\"search-with-fuse-js\">Search with Fuse.js</h2>\n<p>Add a search field to a template and load <a href=\"https://www.fusejs.io\" target=\"_blank\" rel=\"noopener noreferrer\">Fuse.js</a> from a CDN to query the index:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input</span> <span class=\"hljs-attr\">type</span>=<span class=\"hljs-string\">\"search\"</span> <span class=\"hljs-attr\">id</span>=<span class=\"hljs-string\">\"search\"</span> <span class=\"hljs-attr\">placeholder</span>=<span class=\"hljs-string\">\"Search…\"</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul</span> <span class=\"hljs-attr\">id</span>=<span class=\"hljs-string\">\"results\"</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">ul</span>&gt;</span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script</span> <span class=\"hljs-attr\">type</span>=<span class=\"hljs-string\">\"module\"</span>&gt;</span><span class=\"javascript\">\n  <span class=\"hljs-keyword\">import</span> Fuse <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'https://cdn.jsdelivr.net/npm/fuse.js@7/dist/fuse.mjs'</span>;\n\n  <span class=\"hljs-keyword\">const</span> index = <span class=\"hljs-keyword\">await</span> fetch(<span class=\"hljs-string\">'</span></span></span><span class=\"hljs-template-variable\">{{ url('search', {format: 'json'}) }}</span><span class=\"xml\">').then(r =&gt; r.json());\n  const fuse = new Fuse(index, { keys: ['title', 'description', 'content'], threshold: 0.3 });\n  const results = document.getElementById('results');\n\n  document.getElementById('search').addEventListener('input', (e) =&gt; {\n    results.replaceChildren(...fuse.search(e.target.value, { limit: 10 }).map(({ item }) =&gt; {\n      const li = document.createElement('li');\n      const a = document.createElement('a');\n      a.href = item.url;\n      a.textContent = item.title;\n      li.append(a);\n      return li;\n    }));\n  });\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">script</span>&gt;</span></span></code></pre>\n<h2 id=\"alternative-pagefind\">Alternative: Pagefind</h2>\n<p><a href=\"https://pagefind.app\" target=\"_blank\" rel=\"noopener noreferrer\">Pagefind</a> indexes the generated HTML files instead: no JSON template is needed. Run it after the build, on the output directory (<code translate=\"no\">_site</code> by default):</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar build\nnpx pagefind --site _site</code></pre>\n<p>Then load its UI in a template:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link</span> <span class=\"hljs-attr\">href</span>=<span class=\"hljs-string\">\"/pagefind/pagefind-ui.css\"</span> <span class=\"hljs-attr\">rel</span>=<span class=\"hljs-string\">\"stylesheet\"</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/pagefind/pagefind-ui.js\"</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">script</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span> <span class=\"hljs-attr\">id</span>=<span class=\"hljs-string\">\"search\"</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script</span>&gt;</span><span class=\"javascript\">\n  <span class=\"hljs-built_in\">window</span>.addEventListener(<span class=\"hljs-string\">'DOMContentLoaded'</span>, () =&gt; <span class=\"hljs-keyword\">new</span> PagefindUI({ <span class=\"hljs-attr\">element</span>: <span class=\"hljs-string\">'#search'</span> }));\n</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">script</span>&gt;</span></span></code></pre>\n<aside class=\"note note-tip\"><p>The index is a regular page: to regenerate only JSON outputs, declare a <a href=\"/documentation/configuration/#pages-subsets\">subset</a> and run <code translate=\"no\">php cecil.phar build --render-subset=&lt;name&gt;</code>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/deploy-github-pages/",
      "url": "https://cecil-945.pages.dev/how-to/deploy-github-pages/",
      "title": "Deploy to GitHub Pages with GitHub Actions",
      "summary": "Build and deploy a Cecil site to GitHub Pages automatically with the Cecil GitHub Action.",
      "date_published": "2026-10-07T00:00:00+00:00","content_text": "With the Cecil Action, every push to your repository builds the site and publishes it to GitHub Pages, with no server to manage.\nEnable GitHub Pages\nIn your repository, go to Settings → Pages and, under Build and deployment, set Source to GitHub Actions.\nAdd the workflow\nCreate the file .github\/workflows\/build-and-deploy.yml:\nname: Build and deploy to GitHub Pages\non:\n  push:\n    branches: [master, main]\n  workflow_dispatch:\nconcurrency:\n  group: pages\n  cancel-in-progress: true\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout source\n        uses: actions\/checkout@v6\n      - name: Build site\n        uses: Cecilapp\/Cecil-Action@v4\n  deploy:\n    needs: build\n    permissions:\n      pages: write\n      id-token: write\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions\/deploy-pages@v5\nThe build job downloads Cecil, installs themes (if a composer.json file exists), builds the site and uploads the output directory as a Pages artifact. The deploy job then publishes it.\nBase URL\nYou don’t need to change baseurl in cecil.yml: the action builds the site with the URL provided by GitHub Pages (e.g. https:\/\/&lt;user&gt;.github.io\/&lt;repository&gt;\/), using the --baseurl option.\nCustomize the build\nThe action accepts the following optional inputs:\n      - name: Build site\n        uses: Cecilapp\/Cecil-Action@v4\n        with:\n          version: '9.6.2'       # Cecil version (latest by default)\n          install_themes: 'no'   # skip themes installation (`yes` by default)\n          options: '-v --drafts' # build command options (`-v` by default)\nTo speed up builds, you can also restore and save the .cache directory between runs: see the full workflow in the GitHub Pages deployment documentation, and the list of build options.",
      "content_html": "<p>With the <a href=\"https://github.com/Cecilapp/Cecil-Action\" target=\"_blank\" rel=\"noopener noreferrer\">Cecil Action</a>, every push to your repository builds the site and publishes it to <strong>GitHub Pages</strong>, with no server to manage.</p>\n<h2 id=\"enable-github-pages\">Enable GitHub Pages</h2>\n<p>In your repository, go to <strong>Settings</strong> → <strong>Pages</strong> and, under <strong>Build and deployment</strong>, set <strong>Source</strong> to <strong>GitHub Actions</strong>.</p>\n<h2 id=\"add-the-workflow\">Add the workflow</h2>\n<p>Create the file <code translate=\"no\">.github/workflows/build-and-deploy.yml</code>:</p>\n<pre><code class=\"language-yaml 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>\n<span class=\"hljs-attr\">on:</span>\n  <span class=\"hljs-attr\">push:</span>\n    <span class=\"hljs-attr\">branches:</span> <span class=\"hljs-string\">[master,</span> <span class=\"hljs-string\">main]</span>\n  <span class=\"hljs-attr\">workflow_dispatch:</span>\n<span class=\"hljs-attr\">concurrency:</span>\n  <span class=\"hljs-attr\">group:</span> <span class=\"hljs-string\">pages</span>\n  <span class=\"hljs-attr\">cancel-in-progress:</span> <span class=\"hljs-literal\">true</span>\n<span class=\"hljs-attr\">jobs:</span>\n  <span class=\"hljs-attr\">build:</span>\n    <span class=\"hljs-attr\">runs-on:</span> <span class=\"hljs-string\">ubuntu-latest</span>\n    <span class=\"hljs-attr\">steps:</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Checkout</span> <span class=\"hljs-string\">source</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/checkout@v6</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Build</span> <span class=\"hljs-string\">site</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">Cecilapp/Cecil-Action@v4</span>\n  <span class=\"hljs-attr\">deploy:</span>\n    <span class=\"hljs-attr\">needs:</span> <span class=\"hljs-string\">build</span>\n    <span class=\"hljs-attr\">permissions:</span>\n      <span class=\"hljs-attr\">pages:</span> <span class=\"hljs-string\">write</span>\n      <span class=\"hljs-attr\">id-token:</span> <span class=\"hljs-string\">write</span>\n    <span class=\"hljs-attr\">environment:</span>\n      <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">github-pages</span>\n      <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>\n    <span class=\"hljs-attr\">runs-on:</span> <span class=\"hljs-string\">ubuntu-latest</span>\n    <span class=\"hljs-attr\">steps:</span>\n      <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>\n        <span class=\"hljs-attr\">id:</span> <span class=\"hljs-string\">deployment</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/deploy-pages@v5</span></code></pre>\n<p>The <code translate=\"no\">build</code> job downloads Cecil, installs themes (if a <code translate=\"no\">composer.json</code> file exists), builds the site and uploads the output directory as a Pages artifact. The <code translate=\"no\">deploy</code> job then publishes it.</p>\n<h2 id=\"base-url\">Base URL</h2>\n<p>You don’t need to change <code translate=\"no\">baseurl</code> in <code translate=\"no\">cecil.yml</code>: the action builds the site with the URL provided by GitHub Pages (e.g. <code translate=\"no\">https://&lt;user&gt;.github.io/&lt;repository&gt;/</code>), using the <code translate=\"no\">--baseurl</code> option.</p>\n<h2 id=\"customize-the-build\">Customize the build</h2>\n<p>The action accepts the following optional inputs:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\">      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Build</span> <span class=\"hljs-string\">site</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">Cecilapp/Cecil-Action@v4</span>\n        <span class=\"hljs-attr\">with:</span>\n          <span class=\"hljs-attr\">version:</span> <span class=\"hljs-string\">'9.6.2'</span>       <span class=\"hljs-comment\"># Cecil version (latest by default)</span>\n          <span class=\"hljs-attr\">install_themes:</span> <span class=\"hljs-string\">'no'</span>   <span class=\"hljs-comment\"># skip themes installation (`yes` by default)</span>\n          <span class=\"hljs-attr\">options:</span> <span class=\"hljs-string\">'-v --drafts'</span> <span class=\"hljs-comment\"># build command options (`-v` by default)</span></code></pre>\n<aside class=\"note note-tip\"><p>To speed up builds, you can also restore and save the <code translate=\"no\">.cache</code> directory between runs: see the full workflow in the <a href=\"/documentation/deploy/#github-pages\">GitHub Pages deployment documentation</a>, and the list of <a href=\"/documentation/commands/#build\">build options</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/output-rss-json-feed/",
      "url": "https://cecil-945.pages.dev/how-to/output-rss-json-feed/",
      "title": "Publish an RSS or JSON feed",
      "summary": "Publish RSS, Atom or JSON feeds of your Cecil site with output formats and built-in templates.",
      "date_published": "2026-10-06T00:00:00+00:00","content_text": "Cecil renders feeds with output formats: by default, the home page, sections and taxonomy terms are already published in HTML and Atom (e.g. \/atom.xml, \/blog\/atom.xml). Add the rss or jsonfeed formats to publish RSS 2.0 or JSON Feed files too.\nEnable feeds for all list pages\nSet the formats applied to each page type with output.pagetypeformats:\noutput:\n  pagetypeformats:\n    homepage: [html, atom, rss, jsonfeed]\n    section: [html, atom, rss, jsonfeed]\nCecil now generates rss.xml and feed.json next to each index.html of the home page and sections (e.g. \/blog\/rss.xml, \/blog\/feed.json).\nFormats are replaced, not merged: keep html in the list. See output.pagetypeformats and the list of default formats.\nEnable a feed for a single section\nTo publish a feed only for one section, use the output variable in the front matter of the section index page:\n---\ntitle: Blog\noutput: [html, rss]\n---\nAdvertise the feed\nIf your templates include the metatags partial, &lt;link rel=\"alternate\"&gt; tags pointing to the feeds of the current page are added automatically in the &lt;head&gt;.\nOtherwise, add the link yourself with the url() function and its format option:\n&lt;link rel=\"alternate\" type=\"application\/rss+xml\" title=\"{{ site.title }}\" href=\"{{ url(page, {canonical: true, format: 'rss'}) }}\"&gt;\nCustomize the feed template\nFeeds are rendered by the built-in templates _default\/list.rss.twig, _default\/list.atom.twig and _default\/list.jsonfeed.twig. Following the lookup rules, create layouts\/list.rss.twig (all list pages) or layouts\/blog\/list.rss.twig (blog section only) to override them.\nYou can extend the built-in template and redefine only the item block, for example to publish an excerpt instead of the full content:\n{% extends '_default\/list.rss.twig' %}\n\n{% block item %}\n      &lt;guid&gt;{{ url(item, {canonical: true}) }}&lt;\/guid&gt;\n      &lt;title&gt;{{ item.title|e }}&lt;\/title&gt;\n      &lt;pubDate&gt;{{ item.date|date('r') }}&lt;\/pubDate&gt;\n      &lt;link&gt;{{ url(item, {canonical: true}) }}&lt;\/link&gt;\n      &lt;description&gt;&lt;![CDATA[{{ item.content|excerpt_html }}]]&gt;&lt;\/description&gt;\n{% endblock %}\nSet baseurl in cecil.yml: feeds use absolute URLs.\nThe RSS feed can also be styled in browsers by enabling the xsl\/rss default page:\npages:\n  default:\n    xsl\/rss:\n      published: true",
      "content_html": "<p>Cecil renders feeds with <strong>output formats</strong>: by default, the home page, sections and taxonomy terms are already published in HTML <strong>and Atom</strong> (e.g. <code translate=\"no\">/atom.xml</code>, <code translate=\"no\">/blog/atom.xml</code>). Add the <code translate=\"no\">rss</code> or <code translate=\"no\">jsonfeed</code> formats to publish RSS 2.0 or <a href=\"https://www.jsonfeed.org\" target=\"_blank\" rel=\"noopener noreferrer\">JSON Feed</a> files too.</p>\n<h2 id=\"enable-feeds-for-all-list-pages\">Enable feeds for all list pages</h2>\n<p>Set the formats applied to each page type with <code translate=\"no\">output.pagetypeformats</code>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">output:</span>\n  <span class=\"hljs-attr\">pagetypeformats:</span>\n    <span class=\"hljs-attr\">homepage:</span> <span class=\"hljs-string\">[html,</span> <span class=\"hljs-string\">atom,</span> <span class=\"hljs-string\">rss,</span> <span class=\"hljs-string\">jsonfeed]</span>\n    <span class=\"hljs-attr\">section:</span> <span class=\"hljs-string\">[html,</span> <span class=\"hljs-string\">atom,</span> <span class=\"hljs-string\">rss,</span> <span class=\"hljs-string\">jsonfeed]</span></code></pre>\n<p>Cecil now generates <code translate=\"no\">rss.xml</code> and <code translate=\"no\">feed.json</code> next to each <code translate=\"no\">index.html</code> of the home page and sections (e.g. <code translate=\"no\">/blog/rss.xml</code>, <code translate=\"no\">/blog/feed.json</code>).</p>\n<aside class=\"note note-info\"><p>Formats are replaced, not merged: keep <code translate=\"no\">html</code> in the list. See <a href=\"/documentation/configuration/#output-pagetypeformats\"><code translate=\"no\">output.pagetypeformats</code></a> and the list of <a href=\"/documentation/configuration/#output-formats\">default formats</a>.</p></aside>\n<h2 id=\"enable-a-feed-for-a-single-section\">Enable a feed for a single section</h2>\n<p>To publish a feed only for one section, use the <a href=\"/documentation/content/#output\"><code translate=\"no\">output</code></a> variable in the front matter of the section index page:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">Blog</span>\n<span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">[html,</span> <span class=\"hljs-string\">rss]</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<h2 id=\"advertise-the-feed\">Advertise the feed</h2>\n<p>If your templates include the <a href=\"/documentation/configuration/#metatags\">metatags partial</a>, <code translate=\"no\">&lt;link rel=\"alternate\"&gt;</code> tags pointing to the feeds of the current page are added automatically in the <code translate=\"no\">&lt;head&gt;</code>.</p>\n<p>Otherwise, add the link yourself with the <code translate=\"no\">url()</code> function and its <code translate=\"no\">format</code> option:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link</span> <span class=\"hljs-attr\">rel</span>=<span class=\"hljs-string\">\"alternate\"</span> <span class=\"hljs-attr\">type</span>=<span class=\"hljs-string\">\"application/rss+xml\"</span> <span class=\"hljs-attr\">title</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ site.title }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">href</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ url(page, {canonical: true, format: 'rss'}) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span></code></pre>\n<h2 id=\"customize-the-feed-template\">Customize the feed template</h2>\n<p>Feeds are rendered by the <a href=\"/documentation/templates/#built-in-templates\">built-in templates</a> <code translate=\"no\">_default/list.rss.twig</code>, <code translate=\"no\">_default/list.atom.twig</code> and <code translate=\"no\">_default/list.jsonfeed.twig</code>. Following the <a href=\"/documentation/templates/#lookup-rules\">lookup rules</a>, create <code translate=\"no\">layouts/list.rss.twig</code> (all list pages) or <code translate=\"no\">layouts/blog/list.rss.twig</code> (<code translate=\"no\">blog</code> section only) to override them.</p>\n<p>You can extend the built-in template and redefine only the <code translate=\"no\">item</code> block, for example to publish an excerpt instead of the full content:</p>\n<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> '_default/list.rss.twig' %}</span><span class=\"xml\">\n\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">block</span></span> item %}</span><span class=\"xml\">\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">guid</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ url(item, {canonical: true}) }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">guid</span>&gt;</span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ item.title|e }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">title</span>&gt;</span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">pubDate</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ item.<span class=\"hljs-name\">date</span>|<span class=\"hljs-keyword\">date</span>('r') }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">pubDate</span>&gt;</span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ url(item, {canonical: true}) }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">link</span>&gt;</span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">description</span>&gt;</span>&lt;![CDATA[</span><span class=\"hljs-template-variable\">{{ item.content|excerpt_html }}</span><span class=\"xml\">]]&gt;<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">description</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endblock</span></span> %}</span></code></pre>\n<aside class=\"note note-tip\"><p>Set <a href=\"/documentation/configuration/#baseurl\"><code translate=\"no\">baseurl</code></a> in <code translate=\"no\">cecil.yml</code>: feeds use absolute URLs.</p>\n<p>The RSS feed can also be styled in browsers by enabling the <code translate=\"no\">xsl/rss</code> <a href=\"/documentation/configuration/#pages-default\">default page</a>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">default:</span>\n    <span class=\"hljs-attr\">xsl/rss:</span>\n      <span class=\"hljs-attr\">published:</span> <span class=\"hljs-literal\">true</span></code></pre></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/templates-pagination/",
      "url": "https://cecil-945.pages.dev/how-to/templates-pagination/",
      "title": "Paginate a list of pages",
      "summary": "Split long lists of pages (blog, sections, taxonomies) into several pages with Cecil pagination.",
      "date_published": "2026-10-05T00:00:00+00:00","content_text": "When a section contains many pages, Cecil can split its list into several pages (e.g.: \/blog\/, \/blog\/page\/2\/, \/blog\/page\/3\/, etc.) and give you a paginator to build the navigation links.\nConfigure pagination\nPagination is enabled by default for list pages (homepage, section and term), with 5 entries per page. Change it in cecil.yml:\npages:\n  pagination:\n    max: 10    # maximum number of entries per page\n    path: page # path to the paginated page\nOverride it for a section\nSet the pagination variable in the front matter of the section index file (e.g.: pages\/blog\/index.md):\n---\ntitle: Blog\npagination:\n  max: 20\n---\nOr disable it for this section only:\n---\npagination: false\n---\nDisplay the paginated pages\nIn the list template (e.g.: layouts\/blog\/list.html.twig), loop over page.paginator.pages and fall back to page.pages when pagination is disabled:\n{% for p in page.paginator.pages ?? page.pages %}\n  &lt;article&gt;\n    &lt;h2&gt;&lt;a href=\"{{ url(p) }}\"&gt;{{ p.title }}&lt;\/a&gt;&lt;\/h2&gt;\n    &lt;time datetime=\"{{ p.date|date('c') }}\"&gt;{{ p.date|date('j M Y') }}&lt;\/time&gt;\n  &lt;\/article&gt;\n{% endfor %}\nAdd navigation links\nPaginator links are page IDs, so use the url() function to create working links:\n{% if page.paginator %}\n&lt;nav&gt;\n  {% if page.paginator.links.prev is defined %}\n  &lt;a href=\"{{ url(page.paginator.links.prev) }}\" rel=\"prev\"&gt;Previous&lt;\/a&gt;\n  {% endif %}\n  &lt;span&gt;Page {{ page.paginator.current }} of {{ page.paginator.count }}&lt;\/span&gt;\n  {% if page.paginator.links.next is defined %}\n  &lt;a href=\"{{ url(page.paginator.links.next) }}\" rel=\"next\"&gt;Next&lt;\/a&gt;\n  {% endif %}\n&lt;\/nav&gt;\n{% endif %}\nTo list every page number, iterate from 1 to page.paginator.count:\n{% if page.paginator %}\n&lt;nav&gt;\n  {% for i in 1..page.paginator.count %}\n    {% if i == page.paginator.current %}\n  &lt;span aria-current=\"page\"&gt;{{ i }}&lt;\/span&gt;\n    {% elseif i == 1 %}\n  &lt;a href=\"{{ url(page.paginator.links.first) }}\"&gt;{{ i }}&lt;\/a&gt;\n    {% else %}\n  &lt;a href=\"{{ url(page.paginator.links.path ~ '\/' ~ i) }}\"&gt;{{ i }}&lt;\/a&gt;\n    {% endif %}\n  {% endfor %}\n&lt;\/nav&gt;\n{% endif %}\nSee the documentation of the page.paginator variable, the pagination configuration and the section pagination variable.",
      "content_html": "<p>When a section contains many pages, Cecil can split its list into several pages (e.g.: <code translate=\"no\">/blog/</code>, <code translate=\"no\">/blog/page/2/</code>, <code translate=\"no\">/blog/page/3/</code>, etc.) and give you a <em>paginator</em> to build the navigation links.</p>\n<h2 id=\"configure-pagination\">Configure pagination</h2>\n<p>Pagination is enabled by default for list pages (<em>homepage</em>, <em>section</em> and <em>term</em>), with 5 entries per page. Change it in <code translate=\"no\">cecil.yml</code>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">pagination:</span>\n    <span class=\"hljs-attr\">max:</span> <span class=\"hljs-number\">10</span>    <span class=\"hljs-comment\"># maximum number of entries per page</span>\n    <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">page</span> <span class=\"hljs-comment\"># path to the paginated page</span></code></pre>\n<h2 id=\"override-it-for-a-section\">Override it for a section</h2>\n<p>Set the <code translate=\"no\">pagination</code> variable in the front matter of the section index file (e.g.: <code translate=\"no\">pages/blog/index.md</code>):</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">Blog</span>\n<span class=\"hljs-attr\">pagination:</span>\n  <span class=\"hljs-attr\">max:</span> <span class=\"hljs-number\">20</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<p>Or disable it for this section only:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">pagination:</span> <span class=\"hljs-literal\">false</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<h2 id=\"display-the-paginated-pages\">Display the paginated pages</h2>\n<p>In the list template (e.g.: <code translate=\"no\">layouts/blog/list.html.twig</code>), loop over <code translate=\"no\">page.paginator.pages</code> and fall back to <code translate=\"no\">page.pages</code> when pagination is disabled:</p>\n<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> p in page.paginator.pages ?? page.pages %}</span><span class=\"xml\">\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">article</span>&gt;</span>\n    <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>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">time</span> <span class=\"hljs-attr\">datetime</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ p.<span class=\"hljs-name\">date</span>|<span class=\"hljs-keyword\">date</span>('c') }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ p.<span class=\"hljs-name\">date</span>|<span class=\"hljs-keyword\">date</span>('j M Y') }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">time</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">article</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span></code></pre>\n<h2 id=\"add-navigation-links\">Add navigation links</h2>\n<p>Paginator links are page IDs, so use the <code translate=\"no\">url()</code> function to create working links:</p>\n<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\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">nav</span>&gt;</span>\n  </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\">\n  <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> <span class=\"hljs-attr\">rel</span>=<span class=\"hljs-string\">\"prev\"</span>&gt;</span>Previous<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n  </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endif</span></span> %}</span><span class=\"xml\">\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span</span>&gt;</span>Page </span><span class=\"hljs-template-variable\">{{ page.paginator.current }}</span><span class=\"xml\"> of </span><span class=\"hljs-template-variable\">{{ page.paginator.count }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">span</span>&gt;</span>\n  </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\">\n  <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> <span class=\"hljs-attr\">rel</span>=<span class=\"hljs-string\">\"next\"</span>&gt;</span>Next<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n  </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endif</span></span> %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">nav</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endif</span></span> %}</span></code></pre>\n<p>To list every page number, iterate from <code translate=\"no\">1</code> to <code translate=\"no\">page.paginator.count</code>:</p>\n<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\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">nav</span>&gt;</span>\n  </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">for</span></span> i in 1..page.paginator.count %}</span><span class=\"xml\">\n    </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">if</span></span> i == page.paginator.current %}</span><span class=\"xml\">\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span</span> <span class=\"hljs-attr\">aria-current</span>=<span class=\"hljs-string\">\"page\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ i }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">span</span>&gt;</span>\n    </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">elseif</span> i == 1 %}</span><span class=\"xml\">\n  <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\">{{ i }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n    </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">else</span> %}</span><span class=\"xml\">\n  <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 ~ '/' ~ i) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ i }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n    </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endif</span></span> %}</span><span class=\"xml\">\n  </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">nav</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endif</span></span> %}</span></code></pre>\n<aside class=\"note note-info\"><p>See the documentation of the <a href=\"/documentation/templates/#page\"><code translate=\"no\">page.paginator</code> variable</a>, the <a href=\"/documentation/configuration/#pages-pagination\">pagination configuration</a> and the <a href=\"/documentation/content/#section\">section <code translate=\"no\">pagination</code> variable</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/content-multilingual/",
      "url": "https://cecil-945.pages.dev/how-to/content-multilingual/",
      "title": "Translate a site into multiple languages",
      "summary": "Translate pages, configuration and template strings of a Cecil site into multiple languages.",
      "date_published": "2026-10-04T00:00:00+00:00","content_text": "Cecil handles multilingual sites natively: declare the languages, add translated pages, then link them together and translate your templates.\nDeclare languages\nDefine the main language and the list of available languages in cecil.yml:\nlanguage: en\nlanguages:\n  - code: en\n    name: English\n    locale: en_US\n  - code: fr\n    name: Français\n    locale: fr_FR\n    config:\n      title: \"Mon site en français\"\nOptions stored under the config key of a language override the global ones (here the site title).\nTranslate a page\nDuplicate the reference page and suffix its file name with the language code:\npages\/\n├─ about.md    # the reference page\n└─ about.fr.md # the french version\nUse the slug variable to translate the URL of the page:\n---\ntitle: À propos\nslug: a-propos\n---\nabout.md is published to \/about\/ and about.fr.md to \/fr\/a-propos\/.\nTo create a page that only exists in another language (not a translation), set language: fr in its front matter.\nLink translated pages\nEach page exposes its translations with page.translations. Add a language switcher to your template:\n{% for p in page.translations %}\n  &lt;a href=\"{{ url(p) }}\" hreflang=\"{{ p.language }}\"&gt;{{ site.language.name(p.language) }}&lt;\/a&gt;\n{% endfor %}\nYou can also include the built-in partial: {{ include('partials\/languages.html.twig') }}.\nTranslate template strings\nWrap texts with the trans tag or filter:\n{% trans %}Read more{% endtrans %}\n{{ 'Read more'|trans }}\nThen add a translation file named after the language locale in the translations directory:\n# translations\/messages.fr_FR.yaml\nRead more: Lire la suite\nExtract the strings of your templates with:\nphp cecil.phar util:translations:extract --locale=fr_FR --save\nSee the documentation of multilingual content, languages configuration and templates localization.",
      "content_html": "<p>Cecil handles multilingual sites natively: declare the languages, add translated pages, then link them together and translate your templates.</p>\n<h2 id=\"declare-languages\">Declare languages</h2>\n<p>Define the main language and the list of available languages in <code translate=\"no\">cecil.yml</code>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">language:</span> <span class=\"hljs-string\">en</span>\n<span class=\"hljs-attr\">languages:</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">code:</span> <span class=\"hljs-string\">en</span>\n    <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">English</span>\n    <span class=\"hljs-attr\">locale:</span> <span class=\"hljs-string\">en_US</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">code:</span> <span class=\"hljs-string\">fr</span>\n    <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Français</span>\n    <span class=\"hljs-attr\">locale:</span> <span class=\"hljs-string\">fr_FR</span>\n    <span class=\"hljs-attr\">config:</span>\n      <span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">\"Mon site en français\"</span></code></pre>\n<p>Options stored under the <code translate=\"no\">config</code> key of a language override the global ones (here the site <code translate=\"no\">title</code>).</p>\n<h2 id=\"translate-a-page\">Translate a page</h2>\n<p>Duplicate the reference page and suffix its file name with the language code:</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">pages/\n├─ about.md    # the reference page\n└─ about.fr.md # the french version</code></pre>\n<p>Use the <code translate=\"no\">slug</code> variable to translate the URL of the page:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">À</span> <span class=\"hljs-string\">propos</span>\n<span class=\"hljs-attr\">slug:</span> <span class=\"hljs-string\">a-propos</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<p><code translate=\"no\">about.md</code> is published to <code translate=\"no\">/about/</code> and <code translate=\"no\">about.fr.md</code> to <code translate=\"no\">/fr/a-propos/</code>.</p>\n<aside class=\"note note-tip\"><p>To create a page that only exists in another language (not a translation), set <code translate=\"no\">language: fr</code> in its front matter.</p></aside>\n<h2 id=\"link-translated-pages\">Link translated pages</h2>\n<p>Each page exposes its translations with <code translate=\"no\">page.translations</code>. Add a language switcher to your template:</p>\n<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> p in page.translations %}</span><span class=\"xml\">\n  <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> <span class=\"hljs-attr\">hreflang</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ p.language }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ site.language.name(p.language) }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span></code></pre>\n<p>You can also include the built-in partial: <code translate=\"no\">{{ include('partials/languages.html.twig') }}</code>.</p>\n<h2 id=\"translate-template-strings\">Translate template strings</h2>\n<p>Wrap texts with the <code translate=\"no\">trans</code> tag or filter:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">trans</span> %}</span><span class=\"xml\">Read more</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endtrans</span> %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ 'Read more'|trans }}</span></code></pre>\n<p>Then add a translation file named after the language locale in the <code translate=\"no\">translations</code> directory:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-comment\"># translations/messages.fr_FR.yaml</span>\n<span class=\"hljs-attr\">Read more:</span> <span class=\"hljs-string\">Lire</span> <span class=\"hljs-string\">la</span> <span class=\"hljs-string\">suite</span></code></pre>\n<p>Extract the strings of your templates with:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar util:translations:extract --locale=fr_FR --save</code></pre>\n<aside class=\"note note-info\"><p>See the documentation of <a href=\"/documentation/content/#multilingual\">multilingual content</a>, <a href=\"/documentation/configuration/#languages\">languages configuration</a> and <a href=\"/documentation/templates/#localization\">templates localization</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/templates-metatags/",
      "url": "https://cecil-945.pages.dev/how-to/templates-metatags/",
      "title": "Apply SEO features in templates",
      "summary": "Add Cecil SEO features to templates with the metatags partial.",
      "date_published": "2026-06-09T00:00:00+00:00","content_text": "Cecil exposes its SEO feature through the partials\/metatags.html.twig template. Include it in the &lt;head&gt; of your base template so every page automatically gets meta tags, canonical links, social cards, and structured data.\n&lt;html lang=\"{{ site.language }}\"&gt;\n  &lt;head&gt;\n    &lt;meta charset=\"utf-8\"&gt;\n    {{ include('partials\/metatags.html.twig') }}\n  &lt;\/head&gt;\n  &lt;body&gt;\n    ...\n  &lt;\/body&gt;\n&lt;\/html&gt;\nThe partial reads page front matter first, then falls back to site configuration when needed. See the metatags configuration documentation for all available options.\nIf you need to override the default title or image, pass values directly to the partial:\n{{ include('partials\/metatags.html.twig', {title: 'Custom title', image: og_image}) }}",
      "content_html": "<p>Cecil exposes its SEO feature through the <code translate=\"no\">partials/metatags.html.twig</code> template. Include it in the <code translate=\"no\">&lt;head&gt;</code> of your base template so every page automatically gets <strong>meta tags, canonical links, social cards, and structured data</strong>.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html</span> <span class=\"hljs-attr\">lang</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ site.language }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta</span> <span class=\"hljs-attr\">charset</span>=<span class=\"hljs-string\">\"utf-8\"</span>&gt;</span>\n    </span><span class=\"hljs-template-variable\">{{ <span class=\"hljs-name\">include</span><span class=\"hljs-params\">('partials/metatags.html.twig')</span> }}</span><span class=\"xml\">\n  <span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">head</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body</span>&gt;</span>\n    ...\n  <span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">body</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">html</span>&gt;</span></span></code></pre>\n<p>The partial reads page front matter first, then falls back to site configuration when needed. See the <a href=\"/documentation/configuration/#metatags\">metatags configuration documentation</a> for all available options.</p>\n<p>If you need to override the default title or image, pass values directly to the partial:</p>\n<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>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/markdown-performance-images/",
      "url": "https://cecil-945.pages.dev/how-to/markdown-performance-images/",
      "title": "Optimize images in Markdown",
      "summary": "Optimize Markdown images in Cecil with responsive sizes, formats, and lazy loading.",
      "date_published": "2025-06-01T00:00:00+00:00",
      "date_modified": "2025-06-02T00:00:00+00:00","content_text": "One of the most effective ways to improve the performance of your website is to optimize images.\nCecil can manage the following optimizations for images in Markdown, automatically:\n\nDimensions: Images dimensions are set to ensure proper layout and prevent layout shifts\nCompression: The image is compressed to reduce file size without significant loss of quality\nImage formats: Cecil generates AVIF and WebP formats for the image\nResponsive images: Cecil generates two different sizes of the image (based on the configuration) to serve the appropriate size for different devices\nLazy loading: The image is set to load lazily, meaning it will only load when it comes into the viewport\nDecoding: The image is set to decode asynchronously, improving the initial page load time\nPlaceholder: A color placeholder is used while the image is loading\n\nSee documentation for more details on the global assets configuration and the pages configuration.\nExample\nBellow an example with a PNG image 1920x1276 pixels.\n![A star forming region in the sky](..\/..\/assets\/arnaud-girault-IjEtFjxXweE-unsplash.jpg \"Photo by Arnaud Girault\"){placeholder=color}\n\n\n\n\n\n\nPhoto by Arnaud Girault\n\nConfiguration\n# assets global configuration\nassets:\n  images:\n    optimize: true\n    responsive:\n      widths: [768, 1024]\n# configuration of images in Markdown\npages:\n  body:\n    images:\n      formats: [avif, webp]\n      responsive: true\n      lazy: true\n      decoding: true\n      placeholder: color\nGenerated HTML\n&lt;picture&gt;\n  &lt;source\n    type=\"image\/avif\"\n    srcset=\"\/thumbnails\/1024\/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.avif 1024w,\n            \/thumbnails\/768\/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.avif 768w\"\n    sizes=\"100vw\"\n    width=\"1024\"\n    height=\"681\"\n  &gt;\n  &lt;source\n    type=\"image\/webp\"\n    srcset=\"\/thumbnails\/1024\/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.webp 1024w,\n            \/thumbnails\/768\/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.webp 768w\"\n    sizes=\"100vw\"\n    width=\"1024\"\n    height=\"681\"\n  &gt;\n  &lt;img src=\"\/thumbnails\/1024\/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.jpg\"\n    alt=\"Photo by Arnaud Girault\"\n    loading=\"lazy\"\n    decoding=\"async\"\n    width=\"1024\"\n    height=\"681\"\n    style=\";max-width:100%;height:auto;background-color:rgb(58, 56, 44);\"\n    srcset=\"\/thumbnails\/1024\/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.jpg 1024w,\n            \/thumbnails\/768\/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.jpg 768w\"\n    sizes=\"100vw\"\n  &gt;\n&lt;\/picture&gt;",
      "content_html": "<p>One of the most effective ways to improve the performance of your website is to optimize images.</p>\n<p>Cecil can manage the following optimizations for images in Markdown, automatically:</p>\n<ol>\n<li><strong>Dimensions</strong>: Images dimensions are set to ensure proper layout and prevent layout shifts</li>\n<li><strong>Compression</strong>: The image is compressed to reduce file size without significant loss of quality</li>\n<li><strong>Image formats</strong>: Cecil generates AVIF and WebP formats for the image</li>\n<li><strong>Responsive images</strong>: Cecil generates two different sizes of the image (based on the configuration) to serve the appropriate size for different devices</li>\n<li><strong>Lazy loading</strong>: The image is set to load lazily, meaning it will only load when it comes into the viewport</li>\n<li><strong>Decoding</strong>: The image is set to decode asynchronously, improving the initial page load time</li>\n<li><strong>Placeholder</strong>: A color placeholder is used while the image is loading</li>\n</ol>\n<aside class=\"note note-info\"><p>See documentation for more details on the <a href=\"/documentation/configuration/#assets-images\">global assets configuration</a> and the <a href=\"/documentation/configuration/#pages-body\">pages configuration</a>.</p></aside>\n<h2 id=\"example\">Example</h2>\n<p>Bellow an example with a PNG image 1920x1276 pixels.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\">![A star forming region in the sky](../../assets/arnaud-girault-IjEtFjxXweE-unsplash.jpg \"Photo by Arnaud Girault\"){placeholder=color}</span></code></pre>\n<figure>\n<picture title=\"Photo by Arnaud Girault\">\n<source type=\"image/avif\" srcset=\"/thumbnails/480x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.avif 480w, /thumbnails/768x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.avif 768w, /thumbnails/1024x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.avif 1024w\" width=\"1024\" height=\"681\" sizes=\"100vw\">\n<source type=\"image/webp\" srcset=\"/thumbnails/480x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.webp 480w, /thumbnails/768x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.webp 768w, /thumbnails/1024x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.webp 1024w\" width=\"1024\" height=\"681\" sizes=\"100vw\">\n<img src=\"/thumbnails/1024x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.jpg\" alt=\"A star forming region in the sky\" loading=\"lazy\" decoding=\"async\" class=\"dark:brightness-90\" width=\"1024\" height=\"681\" style=\";max-width:100%;height:auto;background-color:rgb(20 28 29);\" srcset=\"/thumbnails/480x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.jpg 480w, /thumbnails/768x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.jpg 768w, /thumbnails/1024x/images/examples/arnaud-girault-IjEtFjxXweE-unsplash.9f9b5448197aded5325854d4f3c79652.jpg 1024w\" sizes=\"100vw\">\n</picture>\n<figcaption>Photo by <a href=\"https://unsplash.com/photos/a-star-forming-region-in-the-sky-IjEtFjxXweE\" target=\"_blank\" rel=\"noopener noreferrer\">Arnaud Girault</a></figcaption>\n</figure>\n<h3 id=\"configuration\">Configuration</h3>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-comment\"># assets global configuration</span>\n<span class=\"hljs-attr\">assets:</span>\n  <span class=\"hljs-attr\">images:</span>\n    <span class=\"hljs-attr\">optimize:</span> <span class=\"hljs-literal\">true</span>\n    <span class=\"hljs-attr\">responsive:</span>\n      <span class=\"hljs-attr\">widths:</span> <span class=\"hljs-string\">[768,</span> <span class=\"hljs-number\">1024</span><span class=\"hljs-string\">]</span>\n<span class=\"hljs-comment\"># configuration of images in Markdown</span>\n<span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">body:</span>\n    <span class=\"hljs-attr\">images:</span>\n      <span class=\"hljs-attr\">formats:</span> <span class=\"hljs-string\">[avif,</span> <span class=\"hljs-string\">webp]</span>\n      <span class=\"hljs-attr\">responsive:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">lazy:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">decoding:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">placeholder:</span> <span class=\"hljs-string\">color</span></code></pre>\n<h3 id=\"generated-html\">Generated HTML</h3>\n<pre><code class=\"language-html hljs xml\" translate=\"no\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">picture</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">source</span>\n    <span class=\"hljs-attr\">type</span>=<span class=\"hljs-string\">\"image/avif\"</span>\n    <span class=\"hljs-attr\">srcset</span>=<span class=\"hljs-string\">\"/thumbnails/1024/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.avif 1024w,\n            /thumbnails/768/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.avif 768w\"</span>\n    <span class=\"hljs-attr\">sizes</span>=<span class=\"hljs-string\">\"100vw\"</span>\n    <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"1024\"</span>\n    <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"681\"</span>\n  &gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">source</span>\n    <span class=\"hljs-attr\">type</span>=<span class=\"hljs-string\">\"image/webp\"</span>\n    <span class=\"hljs-attr\">srcset</span>=<span class=\"hljs-string\">\"/thumbnails/1024/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.webp 1024w,\n            /thumbnails/768/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.webp 768w\"</span>\n    <span class=\"hljs-attr\">sizes</span>=<span class=\"hljs-string\">\"100vw\"</span>\n    <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"1024\"</span>\n    <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"681\"</span>\n  &gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/thumbnails/1024/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.jpg\"</span>\n    <span class=\"hljs-attr\">alt</span>=<span class=\"hljs-string\">\"Photo by Arnaud Girault\"</span>\n    <span class=\"hljs-attr\">loading</span>=<span class=\"hljs-string\">\"lazy\"</span>\n    <span class=\"hljs-attr\">decoding</span>=<span class=\"hljs-string\">\"async\"</span>\n    <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"1024\"</span>\n    <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"681\"</span>\n    <span class=\"hljs-attr\">style</span>=<span class=\"hljs-string\">\";max-width:100%;height:auto;background-color:rgb(58, 56, 44);\"</span>\n    <span class=\"hljs-attr\">srcset</span>=<span class=\"hljs-string\">\"/thumbnails/1024/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.jpg 1024w,\n            /thumbnails/768/arnaud-girault-IjEtFjxXweE-unsplash.c0bdd31264ac3d0d364d02bced31038f.jpg 768w\"</span>\n    <span class=\"hljs-attr\">sizes</span>=<span class=\"hljs-string\">\"100vw\"</span>\n  &gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">picture</span>&gt;</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/templates-image-formats/",
      "url": "https://cecil-945.pages.dev/how-to/templates-image-formats/",
      "title": "Generate multiple formats of an image in templates",
      "summary": "Generate AVIF and WebP image formats in Cecil templates.",
      "date_published": "2025-06-01T00:00:00+00:00","content_text": "For performance and bandwidth optimization, Cecil can generate multiple formats of an image, such as AVIF and WebP. This allows the browser to select the best format based on its capabilities:\n{{ html(asset('image.jpg'), attributes={alt: 'Alternative description'}, options={formats: ['avif', 'webp']}) }}\n\nAVIF and WebP are image formats that have superior compression and quality characteristics compared to their older JPEG and PNG counterparts. Encoding your images in these formats rather than JPEG or PNG means that they will load faster and consume less cellular data.\nAVIF is supported in Chrome, Firefox, and Opera and offers smaller file sizes compared to other formats with the same quality settings.\nWebP is supported in the latest versions of Chrome, Firefox, Safari, Edge, and Opera and provides better lossy and lossless compression for images on the web.\n\n– Chrome Lighthouse docs\nExample\nBelow an example of how to generate AVIF and WebP formats of an image:\n{{ html(asset('cecil-logo.png'), attributes={alt: 'Cecil logo'}, options={formats: ['avif', 'webp']}) }}\nGenerated HTML\n&lt;picture&gt;\n  &lt;source type=\"image\/avif\" srcset=\"\/cecil-logo.c1af8a129a0cde81f9b94ffbf452e10b.avif\"&gt;\n  &lt;source type=\"image\/webp\" srcset=\"\/cecil-logo.c1af8a129a0cde81f9b94ffbf452e10b.webp\"&gt;\n  &lt;img src=\"\/cecil-logo.c1af8a129a0cde81f9b94ffbf452e10b.png\" width=\"250\" height=\"250\" alt=\"Cecil logo\"&gt;\n&lt;\/picture&gt;",
      "content_html": "<p>For performance and bandwidth optimization, Cecil can generate multiple formats of an image, such as AVIF and WebP. This allows the browser to select the best format based on its capabilities:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ html(asset('image.jpg'), attributes={alt: 'Alternative description'}, options={formats: ['avif', 'webp']}) }}</span></code></pre>\n<blockquote>\n<p>AVIF and WebP are image formats that have superior compression and quality characteristics compared to their older JPEG and PNG counterparts. Encoding your images in these formats rather than JPEG or PNG means that they will load faster and consume less cellular data.</p>\n<p>AVIF is supported in Chrome, Firefox, and Opera and offers smaller file sizes compared to other formats with the same quality settings.</p>\n<p>WebP is supported in the latest versions of Chrome, Firefox, Safari, Edge, and Opera and provides better lossy and lossless compression for images on the web.</p>\n</blockquote>\n<p>– <a href=\"https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images\" target=\"_blank\" rel=\"noopener noreferrer\">Chrome Lighthouse docs</a></p>\n<h2 id=\"example\">Example</h2>\n<p>Below an example of how to generate AVIF and WebP formats of an image:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ html(asset('cecil-logo.png'), attributes={alt: 'Cecil logo'}, options={formats: ['avif', 'webp']}) }}</span></code></pre>\n<h3 id=\"generated-html\">Generated HTML</h3>\n<pre><code class=\"language-html hljs xml\" translate=\"no\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">picture</span>&gt;</span>\n  <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\">\"/cecil-logo.c1af8a129a0cde81f9b94ffbf452e10b.avif\"</span>&gt;</span>\n  <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\">\"/cecil-logo.c1af8a129a0cde81f9b94ffbf452e10b.webp\"</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/cecil-logo.c1af8a129a0cde81f9b94ffbf452e10b.png\"</span> <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"250\"</span> <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"250\"</span> <span class=\"hljs-attr\">alt</span>=<span class=\"hljs-string\">\"Cecil logo\"</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">picture</span>&gt;</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/how-to/templates-responsive-images/",
      "url": "https://cecil-945.pages.dev/how-to/templates-responsive-images/",
      "title": "Render responsive images in templates",
      "summary": "Render responsive images in Cecil templates with the html function.",
      "date_published": "2024-01-18T00:00:00+00:00",
      "date_modified": "2025-05-12T00:00:00+00:00","content_text": "The easiest way to render responsive images in templates is with the html function:\n{{ html(asset('image.jpg'), attributes={alt: 'Alternative description'}, options={responsive: true}) }}\nThe default width values of the generated images are 480, 640, 768, 1024, 1366, 1600 and 1920. They can be modified in the assets section of the configuration.\nExample\nBelow an example with a PNG image 1000x1000 pixels.\n{{ html(asset('cecil-logo-1000.png'), attributes={alt: 'Cecil logo'}, options={responsive: true}) }}\nConfiguration\nassets:\n  images:\n    responsive:\n      widths: [768, 1024]\nGenerated HTML\n&lt;img\n  alt=\"Cecil logo\"\n  width=\"1000\"\n  height=\"1000\"\n  src=\"\/cecil-logo-1000.fbacb922cddbcdb7ca9a03a3ca3cf2ca.png\"\n  srcset=\"\/thumbnails\/768\/cecil-logo-1000.fbacb922cddbcdb7ca9a03a3ca3cf2ca.png 768w,\n          \/cecil-logo-1000.fbacb922cddbcdb7ca9a03a3ca3cf2ca.png 1000w\"\n  sizes=\"100vw\"\n&gt;",
      "content_html": "<p>The easiest way to render <a href=\"https://developer.mozilla.org/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images\" target=\"_blank\" rel=\"noopener noreferrer\">responsive images</a> in templates is with the <a href=\"/documentation/templates/#html\">html function</a>:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ html(asset('image.jpg'), attributes={alt: 'Alternative description'}, options={responsive: true}) }}</span></code></pre>\n<aside class=\"note note-important\"><p>The default width values of the generated images are 480, 640, 768, 1024, 1366, 1600 and 1920. They can be modified in the <em>assets</em> section of the <a href=\"/documentation/configuration/#assets-images\">configuration</a>.</p></aside>\n<h2 id=\"example\">Example</h2>\n<p>Below an example with a PNG image 1000x1000 pixels.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ html(asset('cecil-logo-1000.png'), attributes={alt: 'Cecil logo'}, options={responsive: true}) }}</span></code></pre>\n<h3 id=\"configuration\">Configuration</h3>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">assets:</span>\n  <span class=\"hljs-attr\">images:</span>\n    <span class=\"hljs-attr\">responsive:</span>\n      <span class=\"hljs-attr\">widths:</span> <span class=\"hljs-string\">[768,</span> <span class=\"hljs-number\">1024</span><span class=\"hljs-string\">]</span></code></pre>\n<h3 id=\"generated-html\">Generated HTML</h3>\n<pre><code class=\"language-html hljs xml\" translate=\"no\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img</span>\n  <span class=\"hljs-attr\">alt</span>=<span class=\"hljs-string\">\"Cecil logo\"</span>\n  <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"1000\"</span>\n  <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"1000\"</span>\n  <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/cecil-logo-1000.fbacb922cddbcdb7ca9a03a3ca3cf2ca.png\"</span>\n  <span class=\"hljs-attr\">srcset</span>=<span class=\"hljs-string\">\"/thumbnails/768/cecil-logo-1000.fbacb922cddbcdb7ca9a03a3ca3cf2ca.png 768w,\n          /cecil-logo-1000.fbacb922cddbcdb7ca9a03a3ca3cf2ca.png 1000w\"</span>\n  <span class=\"hljs-attr\">sizes</span>=<span class=\"hljs-string\">\"100vw\"</span>\n&gt;</span></code></pre>",
      "language": "en"
    }
  ]
}
