{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Cecil - Documentation",
  "home_page_url": "https://cecil-945.pages.dev/documentation/",
  "feed_url": "https://cecil-945.pages.dev/documentation/feed.json",
  "description": "Documentation of the Cecil static site generator.",
  "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/documentation/getting-started/installation/",
      "url": "https://cecil-945.pages.dev/documentation/getting-started/installation/",
      "title": "Installation",
      "summary": "Requirements, installation methods (PHAR, standalone binary, package managers, Composer), update and troubleshooting.",
      "date_published": "2026-10-05T00:00:00+00:00",
      "date_modified": "2026-10-13T00:00:00+00:00","content_text": "Installation\nCecil is distributed as a single executable file, cecil.phar, which runs anywhere PHP is installed, and as standalone binaries that don't require PHP.\nRequirements\n\nPHP 8.3+\nPHP extensions: fileinfo, gd and mbstring\n\nCheck your PHP version and loaded extensions:\nphp -v\nphp -m\nOptional extensions\n\n\n\nExtension\nUsage\n\n\n\n\nintl\nDates localization with other locales than en (improves performance otherwise).\n\n\nimagick\nImage processing, preferred over GD when available.\n\n\nffi\nImage processing with libvips (requires Composer installation).\n\n\n\nDownload the PHAR\nDownload cecil.phar from your terminal:\ncurl -LO https:\/\/cecil.app\/cecil.phar\nThen run it with PHP:\nphp cecil.phar --version\nYou can also download a specific version or the preview version.\nStandalone binary\nIf PHP is not installed, download the standalone binary for your platform from the latest GitHub release. It embeds PHP and the required extensions.\n\n\n\nPlatform\nFile\n\n\n\n\nLinux x64\ncecil-linux-x64\n\n\nLinux ARM64\ncecil-linux-arm64\n\n\nmacOS Intel\ncecil-macos-x64\n\n\nmacOS Apple Silicon\ncecil-macos-arm64\n\n\nWindows x64\ncecil-windows-x64.exe\n\n\n\nFor example, on Linux x64:\ncurl -Lo cecil https:\/\/github.com\/Cecilapp\/Cecil\/releases\/latest\/download\/cecil-linux-x64\nchmod +x cecil\n.\/cecil --version\nOn macOS, binaries are not signed: remove the quarantine attribute before the first run with xattr -d com.apple.quarantine cecil.\nSHA-256 checksums are available in the cecil-binaries.sha256 file of each release, and the build provenance can be verified with gh attestation verify cecil --repo Cecilapp\/Cecil.\nThe binary uses GD for image processing: Imagick and libvips are not available. The self-update command is not available either: download the binary again to update it.\nThe serve command relies on the PHP built-in web server, which the binary doesn't embed: it requires a local PHP executable. Without PHP, run the build command and serve the output directory with another web server.\nInstall globally\nInstalling Cecil globally allows you to run the cecil command from any directory, instead of php cecil.phar.\nmacOS and Linux\nWith Homebrew:\nbrew install cecilapp\/tap\/cecil\nOr manually, by moving the PHAR in a directory of your PATH:\nmv cecil.phar \/usr\/local\/bin\/cecil\nchmod +x \/usr\/local\/bin\/cecil\nWindows\nWith Scoop:\nscoop install https:\/\/cecil.app\/scoop\/cecil.json\nOr manually:\n\nMove cecil.phar in a dedicated directory, like C:\\bin\nRename it from cecil.phar to cecil\nAppend ;C:\\bin to your PATH environment variable\nCreate a wrapping batch script next to it\n\nPHIVE\nWith PHIVE (The PHAR Installation and Verification Environment):\nphive install cecil\nComposer\nWith Composer:\ncomposer global require cecil\/cecil\nMake sure Composer's global binaries directory is in your PATH. Run composer global config bin-dir --absolute to get it.\nTo use Cecil as a dependency of a PHP project, see Library.\nVerify the installation\ncecil --version\nRun cecil list to display the available commands.\nUpdate\nUpdate Cecil to the latest version according to your installation method:\n# PHAR\nphp cecil.phar self-update\n# Homebrew\nbrew upgrade cecilapp\/tap\/cecil\n# Scoop\nscoop update cecil\n# PHIVE\nphive update cecil\n# Composer\ncomposer global update cecil\/cecil\nThe standalone binary is updated by downloading it again.\nThe self-update command (PHAR only) also accepts the following options:\n\n--rollback: reverts to the previous installed version\n--stable: forces an update to the last stable version\n--preview: forces an update to the last unstable version\n\nThe changelog of each release is available on GitHub.\nTroubleshooting\ncommand not found: cecil\nThe installation directory is not in your PATH: add it, or run Cecil with php cecil.phar from the directory containing the PHAR.\nPHP version or extension error\nYour PHP CLI may differ from the one you expect (several versions installed): check it with php -v and php --ini, then enable missing extensions in the loaded php.ini file.\nDiagnose a website\nOnce a website is created, run the doctor command to diagnose its configuration.",
      "content_html": "<h1>Installation</h1>\n<p>Cecil is distributed as a single executable file, <code translate=\"no\">cecil.phar</code>, which runs anywhere PHP is installed, and as <a href=\"#standalone-binary\">standalone binaries</a> that don't require PHP.</p>\n<h2 id=\"requirements\">Requirements</h2>\n<ul>\n<li><a href=\"https://www.php.net/manual/install.php\" target=\"_blank\" rel=\"noopener noreferrer\">PHP</a> 8.3+</li>\n<li>PHP extensions: <code translate=\"no\">fileinfo</code>, <code translate=\"no\">gd</code> and <code translate=\"no\">mbstring</code></li>\n</ul>\n<p>Check your PHP version and loaded extensions:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php -v\nphp -m</code></pre>\n<h3 id=\"optional-extensions\">Optional extensions</h3>\n<table>\n<thead>\n<tr>\n<th>Extension</th>\n<th>Usage</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"https://www.php.net/manual/book.intl.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">intl</code></a></td>\n<td>Dates <a href=\"../templates/5-localization.md\">localization</a> with other locales than <code translate=\"no\">en</code> (improves performance otherwise).</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/manual/book.imagick.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">imagick</code></a></td>\n<td>Image processing, preferred over GD when available.</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/manual/book.ffi.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">ffi</code></a></td>\n<td>Image processing with <a href=\"https://www.libvips.org/\" target=\"_blank\" rel=\"noopener noreferrer\">libvips</a> (requires <a href=\"#composer\">Composer installation</a>).</td>\n</tr>\n</tbody>\n</table>\n<h2 id=\"download-the-phar\">Download the PHAR</h2>\n<p>Download <code translate=\"no\">cecil.phar</code> from your terminal:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">curl -LO https://cecil.app/cecil.phar</code></pre>\n<p>Then run it with PHP:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar --version</code></pre>\n<aside class=\"note note-info\"><p>You can also download a <a href=\"/download/\">specific version or the preview version</a>.</p></aside>\n<h2 id=\"standalone-binary\">Standalone binary</h2>\n<p>If PHP is not installed, download the standalone binary for your platform from the <a href=\"https://github.com/Cecilapp/Cecil/releases/latest\" target=\"_blank\" rel=\"noopener noreferrer\">latest GitHub release</a>. It embeds PHP and the required extensions.</p>\n<table>\n<thead>\n<tr>\n<th>Platform</th>\n<th>File</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Linux x64</td>\n<td><code translate=\"no\">cecil-linux-x64</code></td>\n</tr>\n<tr>\n<td>Linux ARM64</td>\n<td><code translate=\"no\">cecil-linux-arm64</code></td>\n</tr>\n<tr>\n<td>macOS Intel</td>\n<td><code translate=\"no\">cecil-macos-x64</code></td>\n</tr>\n<tr>\n<td>macOS Apple Silicon</td>\n<td><code translate=\"no\">cecil-macos-arm64</code></td>\n</tr>\n<tr>\n<td>Windows x64</td>\n<td><code translate=\"no\">cecil-windows-x64.exe</code></td>\n</tr>\n</tbody>\n</table>\n<p>For example, on Linux x64:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">curl -Lo cecil https://github.com/Cecilapp/Cecil/releases/latest/download/cecil-linux-x64\nchmod +x cecil\n./cecil --version</code></pre>\n<p>On macOS, binaries are not signed: remove the quarantine attribute before the first run with <code translate=\"no\">xattr -d com.apple.quarantine cecil</code>.</p>\n<aside class=\"note note-info\"><p>SHA-256 checksums are available in the <code translate=\"no\">cecil-binaries.sha256</code> file of each release, and the build provenance can be verified with <code translate=\"no\">gh attestation verify cecil --repo Cecilapp/Cecil</code>.</p></aside>\n<aside class=\"note note-important\"><p>The binary uses GD for image processing: Imagick and libvips are not available. The <code translate=\"no\">self-update</code> command is not available either: download the binary again to update it.</p>\n<p>The <code translate=\"no\">serve</code> command relies on the PHP built-in web server, which the binary doesn't embed: it requires a local PHP executable. Without PHP, run the <code translate=\"no\">build</code> command and serve the output directory with another web server.</p></aside>\n<h2 id=\"install-globally\">Install globally</h2>\n<p>Installing Cecil globally allows you to run the <code translate=\"no\">cecil</code> command from any directory, instead of <code translate=\"no\">php cecil.phar</code>.</p>\n<h3 id=\"macos-and-linux\">macOS and Linux</h3>\n<p>With <a href=\"https://brew.sh\" target=\"_blank\" rel=\"noopener noreferrer\">Homebrew</a>:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">brew install cecilapp/tap/cecil</code></pre>\n<p>Or manually, by moving the PHAR in a directory of your <code translate=\"no\">PATH</code>:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">mv cecil.phar /usr/<span class=\"hljs-built_in\">local</span>/bin/cecil\nchmod +x /usr/<span class=\"hljs-built_in\">local</span>/bin/cecil</code></pre>\n<h3 id=\"windows\">Windows</h3>\n<p>With <a href=\"https://scoop.sh\" target=\"_blank\" rel=\"noopener noreferrer\">Scoop</a>:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">scoop install https://cecil.app/scoop/cecil.json</code></pre>\n<p>Or manually:</p>\n<ol>\n<li>Move <code translate=\"no\">cecil.phar</code> in a dedicated directory, like <code translate=\"no\">C:\\bin</code></li>\n<li>Rename it from <code translate=\"no\">cecil.phar</code> to <code translate=\"no\">cecil</code></li>\n<li>Append <code translate=\"no\">;C:\\bin</code> to your <code translate=\"no\">PATH</code> environment variable</li>\n<li>Create a <a href=\"https://raw.githubusercontent.com/Cecilapp/Cecil/main/bin/cecil.bat\" target=\"_blank\" rel=\"noopener noreferrer\">wrapping batch script</a> next to it</li>\n</ol>\n<h3 id=\"phive\">PHIVE</h3>\n<p>With <a href=\"https://phar.io\" target=\"_blank\" rel=\"noopener noreferrer\">PHIVE</a> (The PHAR Installation and Verification Environment):</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">phive install cecil</code></pre>\n<h3 id=\"composer\">Composer</h3>\n<p>With <a href=\"https://getcomposer.org\" target=\"_blank\" rel=\"noopener noreferrer\">Composer</a>:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">composer global require cecil/cecil</code></pre>\n<aside class=\"note note-important\"><p>Make sure Composer's global binaries directory is in your <code translate=\"no\">PATH</code>. Run <code translate=\"no\">composer global config bin-dir --absolute</code> to get it.</p></aside>\n<aside class=\"note note-tip\"><p>To use Cecil as a dependency of a PHP project, see <a href=\"../developers/2-library.md\">Library</a>.</p></aside>\n<h2 id=\"verify-the-installation\">Verify the installation</h2>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">cecil --version</code></pre>\n<aside class=\"note note-info\"><p>Run <code translate=\"no\">cecil list</code> to display the <a href=\"../commands/index.md\">available commands</a>.</p></aside>\n<h2 id=\"update\">Update</h2>\n<p>Update Cecil to the latest version according to your installation method:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\"><span class=\"hljs-comment\"># PHAR</span>\nphp cecil.phar self-update\n<span class=\"hljs-comment\"># Homebrew</span>\nbrew upgrade cecilapp/tap/cecil\n<span class=\"hljs-comment\"># Scoop</span>\nscoop update cecil\n<span class=\"hljs-comment\"># PHIVE</span>\nphive update cecil\n<span class=\"hljs-comment\"># Composer</span>\ncomposer global update cecil/cecil</code></pre>\n<p>The <a href=\"#standalone-binary\">standalone binary</a> is updated by downloading it again.</p>\n<p>The <code translate=\"no\">self-update</code> command (PHAR only) also accepts the following options:</p>\n<ul>\n<li><code translate=\"no\">--rollback</code>: reverts to the previous installed version</li>\n<li><code translate=\"no\">--stable</code>: forces an update to the last stable version</li>\n<li><code translate=\"no\">--preview</code>: forces an update to the last unstable version</li>\n</ul>\n<aside class=\"note note-info\"><p>The changelog of each release is available on <a href=\"https://github.com/Cecilapp/Cecil/releases\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a>.</p></aside>\n<h2 id=\"troubleshooting\">Troubleshooting</h2>\n<h3 id=\"command-not-found-cecil\"><code translate=\"no\">command not found: cecil</code></h3>\n<p>The installation directory is not in your <code translate=\"no\">PATH</code>: add it, or run Cecil with <code translate=\"no\">php cecil.phar</code> from the directory containing the PHAR.</p>\n<h3 id=\"php-version-or-extension-error\">PHP version or extension error</h3>\n<p>Your PHP CLI may differ from the one you expect (several versions installed): check it with <code translate=\"no\">php -v</code> and <code translate=\"no\">php --ini</code>, then enable missing extensions in the loaded <code translate=\"no\">php.ini</code> file.</p>\n<h3 id=\"diagnose-a-website\">Diagnose a website</h3>\n<p>Once a website is created, run the <a href=\"../commands/5-doctor.md\"><code translate=\"no\">doctor</code></a> command to diagnose its configuration.</p>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/developers/architecture/",
      "url": "https://cecil-945.pages.dev/documentation/developers/architecture/",
      "title": "Architecture",
      "summary": "Diagram of the build pipeline and key components.",
      "date_published": "2026-05-27T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Architecture\nThis page gives an overview of Cecil’s build pipeline and its key components.\nDiagram\ngraph TD\n    %% Entry\n    CLI[\"bin\/cecil\\n(CLI entry point)\"]\n    APP[\"Application\\n(Symfony Console)\"]\n    CMD[\"Commands\\nbuild \/ serve \/ new:site\\nnew:page \/ clear \/ show:content\"]\n\n    CLI --&gt; APP --&gt; CMD\n\n    %% Orchestrator\n    BUILDER[\"Builder\\n(Orchestrator)\"]\n    CONFIG[\"Configuration\\n(config.yml + default.php\\n+ themes)\"]\n    CMD --&gt; BUILDER\n    CONFIG --&gt; BUILDER\n\n    %% Pipeline\n    subgraph PIPELINE[\"Build pipeline (steps)\"]\n        S1[\"1. Pages\/Load\\n(Finder -&gt; Markdown files)\"]\n        S2[\"2. Data\/Load\\n(YAML files)\"]\n        S3[\"3. StaticFiles\/Load\\n(load static files)\"]\n        S4[\"4. Pages\/Create\\n(create Page objects)\"]\n        S5[\"5. Pages\/Convert\\n(Markdown -&gt; HTML\\nfront matter)\"]\n        S6[\"6. Taxonomies\/Create\\n(create taxonomies)\"]\n        S7[\"7. Pages\/Generate\\n(generators)\"]\n        S8[\"8. Menus\/Create\\n(create menus)\"]\n        S9[\"9. StaticFiles\/Copy\\n(copy static files)\"]\n        S10[\"10. Pages\/Render\\n(Twig rendering)\"]\n        S11[\"11. Pages\/Save\\n(save pages)\"]\n        S12[\"12. Assets\/Save\\n(save assets)\"]\n        S13[\"13. Optimize\/*\\n(optimize HTML\/CSS\/JS\/Images)\"]\n\n        S1 --&gt; S2 --&gt; S3 --&gt; S4 --&gt; S5 --&gt; S6 --&gt; S7 --&gt; S8 --&gt; S9 --&gt; S10 --&gt; S11 --&gt; S12 --&gt; S13\n    end\n\n    BUILDER --&gt; PIPELINE\n\n    %% Subsystems\n    subgraph COLLECTIONS[\"Collections\"]\n        PC[\"PagesCollection\\n(Page objects)\"]\n        TC[\"TaxonomiesCollection\\n(vocabularies\/terms)\"]\n        MC[\"MenusCollection\"]\n    end\n\n    subgraph GENERATORS[\"Generators (virtual pages)\"]\n        GP[\"Pagination\"]\n        GT[\"Taxonomy\"]\n        GS[\"Section\"]\n        GR[\"Redirect\"]\n        GD[\"DefaultPages (home, 404)\"]\n    end\n\n    subgraph RENDERER[\"Twig rendering\"]\n        TW[\"Twig engine\"]\n        EXT[\"Extensions\\n(Core, Content, Collection)\"]\n        PP[\"PostProcessors\\n(metadata, excerpts, links)\"]\n        TH[\"Themes \/ Layouts\"]\n        TW --&gt; EXT\n        TW --&gt; PP\n        TH --&gt; TW\n    end\n\n    subgraph ASSETS[\"Assets\"]\n        AL[\"Asset locator\"]\n        AC[\"Compiler\\n(SCSS -&gt; CSS)\"]\n        AI[\"Image processor\\n(responsive, WebP, AVIF)\"]\n        AO[\"Optimizer\\n(CSS\/JS minification)\"]\n    end\n\n    subgraph OUTPUT[\"Output (_site\/)\"]\n        HTML[\".html pages\"]\n        CSS2[\"CSS\/JS assets\"]\n        IMG[\"images\"]\n        SF[\"static files\"]\n    end\n\n    S4 --&gt; PC\n    S6 --&gt; TC\n    S8 --&gt; MC\n    S7 --&gt; GENERATORS\n    GENERATORS --&gt; PC\n    S10 --&gt; RENDERER\n    S12 --&gt; ASSETS\n\n    PC --&gt; RENDERER\n    TC --&gt; RENDERER\n    MC --&gt; RENDERER\n\n    RENDERER --&gt; S11\n    S11 --&gt; HTML\n    S12 --&gt; CSS2\n    S12 --&gt; IMG\n    S9 --&gt; SF\n\n    %% Inputs\n    subgraph INPUT[\"Sources\"]\n        MD[\"content\/\\n(Markdown + front matter)\"]\n        DATA[\"data\/\\n(YAML)\"]\n        STATIC[\"static\/\"]\n        LAYOUTS[\"layouts\/\\n(Twig templates)\"]\n    end\n\n    MD --&gt; S1\n    DATA --&gt; S2\n    STATIC --&gt; S3\n    LAYOUTS --&gt; TH\nKey Components Legend\n\n\n\nComponent\nRole\n\n\n\n\nBuilder\nCentral orchestrator that executes steps in sequence\n\n\nConfig\nMerges default + theme + project + CLI configuration\n\n\nSteps\nModular pipeline (13 steps), each with init() \/ canProcess() \/ process()\n\n\nCollections\nPages, Taxonomies, Menus: core data structures\n\n\nGenerators\nCreate virtual pages (pagination, tags, redirects, etc.)\n\n\nRenderer (Twig)\nApplies templates + extensions + post-processors\n\n\nAssets\nCompiles SCSS, optimizes images, fingerprints files\n\n\n",
      "content_html": "<h1>Architecture</h1>\n<p>This page gives an overview of Cecil’s build pipeline and its key components.</p>\n<h2 id=\"diagram\">Diagram</h2>\n<pre><code class=\"language-mermaid\" translate=\"no\">graph TD\n    %% Entry\n    CLI[\"bin/cecil\\n(CLI entry point)\"]\n    APP[\"Application\\n(Symfony Console)\"]\n    CMD[\"Commands\\nbuild / serve / new:site\\nnew:page / clear / show:content\"]\n\n    CLI --&gt; APP --&gt; CMD\n\n    %% Orchestrator\n    BUILDER[\"Builder\\n(Orchestrator)\"]\n    CONFIG[\"Configuration\\n(config.yml + default.php\\n+ themes)\"]\n    CMD --&gt; BUILDER\n    CONFIG --&gt; BUILDER\n\n    %% Pipeline\n    subgraph PIPELINE[\"Build pipeline (steps)\"]\n        S1[\"1. Pages/Load\\n(Finder -&gt; Markdown files)\"]\n        S2[\"2. Data/Load\\n(YAML files)\"]\n        S3[\"3. StaticFiles/Load\\n(load static files)\"]\n        S4[\"4. Pages/Create\\n(create Page objects)\"]\n        S5[\"5. Pages/Convert\\n(Markdown -&gt; HTML\\nfront matter)\"]\n        S6[\"6. Taxonomies/Create\\n(create taxonomies)\"]\n        S7[\"7. Pages/Generate\\n(generators)\"]\n        S8[\"8. Menus/Create\\n(create menus)\"]\n        S9[\"9. StaticFiles/Copy\\n(copy static files)\"]\n        S10[\"10. Pages/Render\\n(Twig rendering)\"]\n        S11[\"11. Pages/Save\\n(save pages)\"]\n        S12[\"12. Assets/Save\\n(save assets)\"]\n        S13[\"13. Optimize/*\\n(optimize HTML/CSS/JS/Images)\"]\n\n        S1 --&gt; S2 --&gt; S3 --&gt; S4 --&gt; S5 --&gt; S6 --&gt; S7 --&gt; S8 --&gt; S9 --&gt; S10 --&gt; S11 --&gt; S12 --&gt; S13\n    end\n\n    BUILDER --&gt; PIPELINE\n\n    %% Subsystems\n    subgraph COLLECTIONS[\"Collections\"]\n        PC[\"PagesCollection\\n(Page objects)\"]\n        TC[\"TaxonomiesCollection\\n(vocabularies/terms)\"]\n        MC[\"MenusCollection\"]\n    end\n\n    subgraph GENERATORS[\"Generators (virtual pages)\"]\n        GP[\"Pagination\"]\n        GT[\"Taxonomy\"]\n        GS[\"Section\"]\n        GR[\"Redirect\"]\n        GD[\"DefaultPages (home, 404)\"]\n    end\n\n    subgraph RENDERER[\"Twig rendering\"]\n        TW[\"Twig engine\"]\n        EXT[\"Extensions\\n(Core, Content, Collection)\"]\n        PP[\"PostProcessors\\n(metadata, excerpts, links)\"]\n        TH[\"Themes / Layouts\"]\n        TW --&gt; EXT\n        TW --&gt; PP\n        TH --&gt; TW\n    end\n\n    subgraph ASSETS[\"Assets\"]\n        AL[\"Asset locator\"]\n        AC[\"Compiler\\n(SCSS -&gt; CSS)\"]\n        AI[\"Image processor\\n(responsive, WebP, AVIF)\"]\n        AO[\"Optimizer\\n(CSS/JS minification)\"]\n    end\n\n    subgraph OUTPUT[\"Output (_site/)\"]\n        HTML[\".html pages\"]\n        CSS2[\"CSS/JS assets\"]\n        IMG[\"images\"]\n        SF[\"static files\"]\n    end\n\n    S4 --&gt; PC\n    S6 --&gt; TC\n    S8 --&gt; MC\n    S7 --&gt; GENERATORS\n    GENERATORS --&gt; PC\n    S10 --&gt; RENDERER\n    S12 --&gt; ASSETS\n\n    PC --&gt; RENDERER\n    TC --&gt; RENDERER\n    MC --&gt; RENDERER\n\n    RENDERER --&gt; S11\n    S11 --&gt; HTML\n    S12 --&gt; CSS2\n    S12 --&gt; IMG\n    S9 --&gt; SF\n\n    %% Inputs\n    subgraph INPUT[\"Sources\"]\n        MD[\"content/\\n(Markdown + front matter)\"]\n        DATA[\"data/\\n(YAML)\"]\n        STATIC[\"static/\"]\n        LAYOUTS[\"layouts/\\n(Twig templates)\"]\n    end\n\n    MD --&gt; S1\n    DATA --&gt; S2\n    STATIC --&gt; S3\n    LAYOUTS --&gt; TH</code></pre>\n<h2 id=\"key-components-legend\">Key Components Legend</h2>\n<table>\n<thead>\n<tr>\n<th>Component</th>\n<th>Role</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Builder</strong></td>\n<td>Central orchestrator that executes steps in sequence</td>\n</tr>\n<tr>\n<td><strong>Config</strong></td>\n<td>Merges default + theme + project + CLI configuration</td>\n</tr>\n<tr>\n<td><strong>Steps</strong></td>\n<td>Modular pipeline (13 steps), each with <code translate=\"no\">init()</code> / <code translate=\"no\">canProcess()</code> / <code translate=\"no\">process()</code></td>\n</tr>\n<tr>\n<td><strong>Collections</strong></td>\n<td>Pages, Taxonomies, Menus: core data structures</td>\n</tr>\n<tr>\n<td><strong>Generators</strong></td>\n<td>Create virtual pages (pagination, tags, redirects, etc.)</td>\n</tr>\n<tr>\n<td><strong>Renderer (Twig)</strong></td>\n<td>Applies templates + extensions + post-processors</td>\n</tr>\n<tr>\n<td><strong>Assets</strong></td>\n<td>Compiles SCSS, optimizes images, fingerprints files</td>\n</tr>\n</tbody>\n</table>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/developers/library/",
      "url": "https://cecil-945.pages.dev/documentation/developers/library/",
      "title": "Library",
      "summary": "Use Cecil as a PHP library: install it with Composer, then build and diagnose programmatically.",
      "date_published": "2023-12-13T00:00:00+00:00",
      "date_modified": "2026-10-03T00:00:00+00:00","content_text": "Library\nCecil provides a simple PHP API to build your website.\nYou can read the API documentation for more details.\nInstallation\ncomposer require cecil\/cecil\nlibvips support\nTo process images with libvips (optional), install the libvips driver in your project:\ncomposer require intervention\/image-driver-vips\nThis driver requires libvips installed on your system and the PHP FFI extension enabled.\nWithout it, Cecil uses Imagick or GD instead.\nUsage\nBuild\nBuild a new website with a custom configuration:\nrequire_once 'vendor\/autoload.php';\n\nuse Cecil\\Builder;\n\n$config = [\n    'title'   =&gt; \"My website\",\n    'baseurl' =&gt; 'https:\/\/domain.tld\/',\n];\n\nBuilder::create($config)-&gt;build();\n\nexec('php -S localhost:8000 -t _site'); \/\/ preview locally\nThe main parameter of the create method should be a PHP array or a Cecil\\Config instance.\nDiagnostic\nYou can also run doctor checks through dedicated domain services, without using CLI commands.\n&lt;?php\n\nrequire_once 'vendor\/autoload.php';\n\nuse Cecil\\Builder;\nuse Cecil\\Doctor\\SeoDoctor;\nuse Cecil\\Doctor\\SiteDoctor;\n\n$builder = Builder::create(require 'config.php')\n    -&gt;setSourceDir(__DIR__)\n    -&gt;setDestinationDir(__DIR__);\n\n$siteDoctor = new SiteDoctor();\n$diagnosis = $siteDoctor-&gt;diagnose($builder, __DIR__, ['cecil.yml']);\n\n$seoDoctor = new SeoDoctor();\n$seoAudit = $seoDoctor-&gt;audit($builder, [\n    'page' =&gt; '',\n    'include_virtual' =&gt; false,\n]);\n\nvar_dump($diagnosis['errors'], $seoAudit['summary']);",
      "content_html": "<h1>Library</h1>\n<p>Cecil provides a simple PHP API to build your website.</p>\n<p>You can read the <a href=\"https://cecil.app/documentation/library/api/namespaces/cecil.html\" target=\"_blank\" rel=\"noopener noreferrer\">API documentation</a> for more details.</p>\n<h2 id=\"installation\">Installation</h2>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">composer require cecil/cecil</code></pre>\n<h3 id=\"libvips-support\">libvips support</h3>\n<p>To process images with <a href=\"https://www.libvips.org/\" target=\"_blank\" rel=\"noopener noreferrer\">libvips</a> (optional), install the libvips driver in your project:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">composer require intervention/image-driver-vips</code></pre>\n<aside class=\"note note-important\"><p>This driver requires <a href=\"https://www.libvips.org/install.html\" target=\"_blank\" rel=\"noopener noreferrer\">libvips</a> installed on your system and the PHP <a href=\"https://www.php.net/manual/book.ffi.php\" target=\"_blank\" rel=\"noopener noreferrer\">FFI</a> extension enabled.<br>\nWithout it, Cecil uses <a href=\"https://www.php.net/manual/book.imagick.php\" target=\"_blank\" rel=\"noopener noreferrer\">Imagick</a> or <a href=\"https://www.php.net/manual/book.image.php\" target=\"_blank\" rel=\"noopener noreferrer\">GD</a> instead.</p></aside>\n<h2 id=\"usage\">Usage</h2>\n<h3 id=\"build\">Build</h3>\n<p>Build a new website with a custom configuration:</p>\n<pre><code class=\"language-php hljs php\" translate=\"no\"><span class=\"hljs-keyword\">require_once</span> <span class=\"hljs-string\">'vendor/autoload.php'</span>;\n\n<span class=\"hljs-keyword\">use</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Builder</span>;\n\n$config = [\n    <span class=\"hljs-string\">'title'</span>   =&gt; <span class=\"hljs-string\">\"My website\"</span>,\n    <span class=\"hljs-string\">'baseurl'</span> =&gt; <span class=\"hljs-string\">'https://domain.tld/'</span>,\n];\n\nBuilder::create($config)-&gt;build();\n\nexec(<span class=\"hljs-string\">'php -S localhost:8000 -t _site'</span>); <span class=\"hljs-comment\">// preview locally</span></code></pre>\n<aside class=\"note note-info\"><p>The main parameter of the <code translate=\"no\">create</code> method should be a PHP <code translate=\"no\">array</code> or a <a href=\"https://github.com/Cecilapp/Cecil/blob/main/src/Config.php\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">Cecil\\Config</code></a> instance.</p></aside>\n<h3 id=\"diagnostic\">Diagnostic</h3>\n<p>You can also run doctor checks through dedicated domain services, without using CLI commands.</p>\n<pre><code class=\"language-php hljs php\" translate=\"no\"><span class=\"hljs-meta\">&lt;?php</span>\n\n<span class=\"hljs-keyword\">require_once</span> <span class=\"hljs-string\">'vendor/autoload.php'</span>;\n\n<span class=\"hljs-keyword\">use</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Builder</span>;\n<span class=\"hljs-keyword\">use</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Doctor</span>\\<span class=\"hljs-title\">SeoDoctor</span>;\n<span class=\"hljs-keyword\">use</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Doctor</span>\\<span class=\"hljs-title\">SiteDoctor</span>;\n\n$builder = Builder::create(<span class=\"hljs-keyword\">require</span> <span class=\"hljs-string\">'config.php'</span>)\n    -&gt;setSourceDir(<span class=\"hljs-keyword\">__DIR__</span>)\n    -&gt;setDestinationDir(<span class=\"hljs-keyword\">__DIR__</span>);\n\n$siteDoctor = <span class=\"hljs-keyword\">new</span> SiteDoctor();\n$diagnosis = $siteDoctor-&gt;diagnose($builder, <span class=\"hljs-keyword\">__DIR__</span>, [<span class=\"hljs-string\">'cecil.yml'</span>]);\n\n$seoDoctor = <span class=\"hljs-keyword\">new</span> SeoDoctor();\n$seoAudit = $seoDoctor-&gt;audit($builder, [\n    <span class=\"hljs-string\">'page'</span> =&gt; <span class=\"hljs-string\">''</span>,\n    <span class=\"hljs-string\">'include_virtual'</span> =&gt; <span class=\"hljs-keyword\">false</span>,\n]);\n\nvar_dump($diagnosis[<span class=\"hljs-string\">'errors'</span>], $seoAudit[<span class=\"hljs-string\">'summary'</span>]);</code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/assets/cdn-providers/",
      "url": "https://cecil-945.pages.dev/documentation/assets/cdn-providers/",
      "title": "CDN providers",
      "summary": "Configuration examples of image CDN providers (Cloudinary, Cloudimage, TwicPics, imgix, Netlify Image CDN).",
      "date_published": "2023-10-23T00:00:00+00:00",
      "date_modified": "2026-10-02T00:00:00+00:00","content_text": "CDN providers\nExamples of CDN providers configuration.\nCloudinary\nhttps:\/\/cloudinary.com\nassets:\n  images:\n    cdn:\n      enabled: true\n      account: 'xxxx'\n      url: 'https:\/\/res.cloudinary.com\/%account%\/image\/fetch\/c_limit,w_%width%,q_%quality%,f_%format%,d_default\/%image_url%'\nCloudimage\nhttps:\/\/www.cloudimage.io\nassets:\n  images:\n    cdn:\n      enabled: true\n      account: 'xxxx'\n      url: 'https:\/\/%account%.cloudimg.io\/%image_url%?w=%width%&amp;q=%quality%&amp;force_format=%format%'\nTwicPics\nhttps:\/\/www.twicpics.com\nassets:\n  images:\n    cdn:\n      enabled: true\n      account: 'xxxx'\n      canonical: false\n      remote: false\n      url: 'https:\/\/%account%.twic.pics\/%image_url%?twic=v1\/resize=%width%\/quality=%quality%\/output=%format%'\nSource URL: Your website baseurl.\nimgix\nhttps:\/\/imgix.com\nassets:\n  images:\n    cdn:\n      enabled: true\n      account: 'xxxx'\n      canonical: false\n      remote: false\n      url: 'https:\/\/%account%.imgix.net\/%image_url%?w=%width%&amp;q=%quality%&amp;fm=%format%'\nBase URL: Your website baseurl.\nNetlify Image CDN\nhttps:\/\/docs.netlify.com\/image-cdn\/overview\/\nassets:\n  images:\n    cdn:\n      enabled: true\n      canonical: false\n      url: '\/.netlify\/images?url=%image_url%&amp;w=%width%&amp;fm=%format%'\nRun locally\nSetup Netlify CLI\nnpm install netlify-cli -g\nnetlify link\nnetlify.toml:\n[dev]\n  targetPort = 8000\nRun local server\nphp cecil.phar serve &amp; netlify dev\nOpen http:\/\/localhost:8888",
      "content_html": "<h1>CDN providers</h1>\n<p>Examples of CDN providers <a href=\"../configuration/6-assets.md#assets-images-cdn\"><code translate=\"no\">configuration</code></a>.</p>\n<h2 id=\"cloudinary\">Cloudinary</h2>\n<p><a href=\"https://cloudinary.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://cloudinary.com</a></p>\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\">cdn:</span>\n      <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">account:</span> <span class=\"hljs-string\">'xxxx'</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">'https://res.cloudinary.com/%account%/image/fetch/c_limit,w_%width%,q_%quality%,f_%format%,d_default/%image_url%'</span></code></pre>\n<h2 id=\"cloudimage\">Cloudimage</h2>\n<p><a href=\"https://www.cloudimage.io\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.cloudimage.io</a></p>\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\">cdn:</span>\n      <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">account:</span> <span class=\"hljs-string\">'xxxx'</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">'https://%account%.cloudimg.io/%image_url%?w=%width%&amp;q=%quality%&amp;force_format=%format%'</span></code></pre>\n<h2 id=\"twicpics\">TwicPics</h2>\n<p><a href=\"https://www.twicpics.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.twicpics.com</a></p>\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\">cdn:</span>\n      <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">account:</span> <span class=\"hljs-string\">'xxxx'</span>\n      <span class=\"hljs-attr\">canonical:</span> <span class=\"hljs-literal\">false</span>\n      <span class=\"hljs-attr\">remote:</span> <span class=\"hljs-literal\">false</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">'https://%account%.twic.pics/%image_url%?twic=v1/resize=%width%/quality=%quality%/output=%format%'</span></code></pre>\n<p><code translate=\"no\">Source URL</code>: Your website <code translate=\"no\">baseurl</code>.</p>\n<h2 id=\"imgix\">imgix</h2>\n<p><a href=\"https://imgix.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://imgix.com</a></p>\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\">cdn:</span>\n      <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">account:</span> <span class=\"hljs-string\">'xxxx'</span>\n      <span class=\"hljs-attr\">canonical:</span> <span class=\"hljs-literal\">false</span>\n      <span class=\"hljs-attr\">remote:</span> <span class=\"hljs-literal\">false</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">'https://%account%.imgix.net/%image_url%?w=%width%&amp;q=%quality%&amp;fm=%format%'</span></code></pre>\n<p><code translate=\"no\">Base URL</code>: Your website <code translate=\"no\">baseurl</code>.</p>\n<h2 id=\"netlify-image-cdn\">Netlify Image CDN</h2>\n<p><a href=\"https://docs.netlify.com/image-cdn/overview/\" target=\"_blank\" rel=\"noopener noreferrer\">https://docs.netlify.com/image-cdn/overview/</a></p>\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\">cdn:</span>\n      <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">canonical:</span> <span class=\"hljs-literal\">false</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">'/.netlify/images?url=%image_url%&amp;w=%width%&amp;fm=%format%'</span></code></pre>\n<h3 id=\"run-locally\">Run locally</h3>\n<h4>Setup Netlify CLI</h4>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">npm install netlify-cli -g\nnetlify link</code></pre>\n<p><code translate=\"no\">netlify.toml</code>:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-string\">[dev]</span>\n  <span class=\"hljs-string\">targetPort</span> <span class=\"hljs-string\">=</span> <span class=\"hljs-number\">8000</span></code></pre>\n<h4>Run local server</h4>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar serve &amp; netlify dev</code></pre>\n<p>Open <a href=\"http://localhost:8888\" target=\"_blank\" rel=\"noopener noreferrer\">http://localhost:8888</a></p>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/developers/extend/",
      "url": "https://cecil-945.pages.dev/documentation/developers/extend/",
      "title": "Extend",
      "summary": "Create custom pages generators, add Twig extensions and post-process output files.",
      "date_published": "2023-04-17T00:00:00+00:00",
      "date_modified": "2026-10-02T00:00:00+00:00","content_text": "Extend\nBecause Cecil is powered by PHP it's easy to extend its capabilities.\nPages Generator\nA generator helps you create pages without Markdown files (for example, with data from an API or a database) or alter existing pages.\nJust create a new PHP class in the Cecil\\Generator namespace and add the class name to the pages.generators list.\nExample:\n\/extensions\/Cecil\/Generator\/DummyPage.php\n&lt;?php\nnamespace Cecil\\Generator;\n\nuse Cecil\\Collection\\Page\\Page;\nuse Cecil\\Collection\\Page\\Type;\n\nclass DummyPage extends AbstractGenerator implements GeneratorInterface\n{\n    public function generate(): void\n    {\n        \/\/ create a new page $page, then add it to the site collection\n        $page = (new Page('my-page'))\n            -&gt;setType(Type::PAGE-&gt;value)\n            -&gt;setPath('mypage')\n            -&gt;setBodyHtml('&lt;p&gt;My page body&lt;\/p&gt;')\n            -&gt;setVariable('language', 'en')\n            -&gt;setVariable('title', 'My page')\n            -&gt;setVariable('date', now())\n            -&gt;setVariable('menu', ['main' =&gt; ['weight' =&gt; 99]]);\n        $this-&gt;generatedPages-&gt;add($page);\n    }\n}\n\/extensions\/Cecil\/Generator\/Database.php\n&lt;?php\nnamespace Cecil\\Generator;\n\nuse Cecil\\Collection\\Page\\Page;\nuse Cecil\\Collection\\Page\\Type;\n\nclass Database extends AbstractGenerator implements GeneratorInterface\n{\n    public function generate(): void\n    {\n        \/\/ create pages from a SQLite database\n        $db = new SQLite3('database.sqlite');\n        $statement = $db-&gt;prepare('SELECT * FROM blog');\n        $result = $statement-&gt;execute();\n        while ($row = $result-&gt;fetchArray(SQLITE3_ASSOC)) {\n            $page = (new Page($row['page-id']))\n                -&gt;setType(Type::PAGE-&gt;value)\n                -&gt;setPath($row['path'])\n                -&gt;setBodyHtml($row['html'])\n                -&gt;setVariable('title', $row['title'])\n                -&gt;setVariable('date', $row['date']);\n            $this-&gt;generatedPages-&gt;add($page);\n        }\n        $result-&gt;finalize();\n        $db-&gt;close();\n    }\n}\nconfiguration\npages:\n  generators:\n    # priority: class name\n    99: Cecil\\Generator\\DummyPage\n    35: Cecil\\Generator\\Database\nTwig extension\nYou can add custom functions and filters:\n\ncreate a Twig extension in the Cecil\\Renderer\\Extension namespace\nadd the PHP file in the extensions directory\nadd the class name to the configuration\n\nExample:\n\/extensions\/Cecil\/Renderer\/Extension\/MyTwigExtension.php\n&lt;?php\nnamespace Cecil\\Renderer\\Extension;\n\nclass MyTwigExtension extends \\Twig\\Extension\\AbstractExtension\n{\n    public function getFilters()\n    {\n        \/\/ add a new filter named 'md5'\n        return [\n            new \\Twig\\TwigFilter('md5', 'md5'),\n        ];\n    }\n}\nconfiguration\nlayouts:\n  extensions:\n    MyExtension: Cecil\\Renderer\\Extension\\MyTwigExtension\nOutput Post Processor\nYou can post process page output.\nJust create a new PHP class in the Cecil\\Renderer\\PostProcessor namespace and add the class name to the `output.postprocessors list.\nExample:\n\/extensions\/Cecil\/Renderer\/PostProcessor\/MyProcessor.php\n&lt;?php\nnamespace Cecil\\Renderer\\PostProcessor;\n\nuse Cecil\\Collection\\Page\\Page;\n\nclass MyProcessor extends AbstractPostProcessor\n{\n    public function process(Page $page, string $output, string $format): string\n    {\n        \/\/ add a meta tag to the head of the HTML output\n        if ($format == 'html') {\n            if (!preg_match('\/&lt;meta name=\"test\".*\/i', $output)) {\n                $meta = \\sprintf('&lt;meta name=\"test\" content=\"Test\"&gt;');\n                $output = preg_replace_callback('\/([[:blank:]]*)(&lt;\\\/head&gt;)\/i', function ($matches) use ($meta) {\n                    return str_repeat($matches[1] ?: ' ', 2) . $meta . \"\\n\" . $matches[1] . $matches[2];\n                }, $output);\n            }\n        }\n\n        return $output;\n    }\n}\nconfiguration\noutput:\n  postprocessors:\n    MyProcessor: Cecil\\Renderer\\PostProcessor\\MyProcessor",
      "content_html": "<h1>Extend</h1>\n<p>Because Cecil is powered by PHP it's easy to extend its capabilities.</p>\n<h2 id=\"pages-generator\">Pages Generator</h2>\n<p>A generator helps you create pages without Markdown files (for example, with data from an API or a database) or alter existing pages.</p>\n<p>Just create a new PHP class in the <code translate=\"no\">Cecil\\Generator</code> namespace and add the class name to the <a href=\"../configuration/4-pages.md#pages-generators\"><code translate=\"no\">pages.generators</code></a> list.</p>\n<p><strong>Example:</strong></p>\n<p><em>/extensions/Cecil/Generator/DummyPage.php</em></p>\n<pre><code class=\"language-php hljs php\" translate=\"no\"><span class=\"hljs-meta\">&lt;?php</span>\n<span class=\"hljs-keyword\">namespace</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Generator</span>;\n\n<span class=\"hljs-keyword\">use</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Collection</span>\\<span class=\"hljs-title\">Page</span>\\<span class=\"hljs-title\">Page</span>;\n<span class=\"hljs-keyword\">use</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Collection</span>\\<span class=\"hljs-title\">Page</span>\\<span class=\"hljs-title\">Type</span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">DummyPage</span> <span class=\"hljs-keyword\">extends</span> <span class=\"hljs-title\">AbstractGenerator</span> <span class=\"hljs-keyword\">implements</span> <span class=\"hljs-title\">GeneratorInterface</span>\n</span>{\n    <span class=\"hljs-keyword\">public</span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">generate</span><span class=\"hljs-params\">()</span>: <span class=\"hljs-title\">void</span>\n    </span>{\n        <span class=\"hljs-comment\">// create a new page $page, then add it to the site collection</span>\n        $page = (<span class=\"hljs-keyword\">new</span> Page(<span class=\"hljs-string\">'my-page'</span>))\n            -&gt;setType(Type::PAGE-&gt;value)\n            -&gt;setPath(<span class=\"hljs-string\">'mypage'</span>)\n            -&gt;setBodyHtml(<span class=\"hljs-string\">'&lt;p&gt;My page body&lt;/p&gt;'</span>)\n            -&gt;setVariable(<span class=\"hljs-string\">'language'</span>, <span class=\"hljs-string\">'en'</span>)\n            -&gt;setVariable(<span class=\"hljs-string\">'title'</span>, <span class=\"hljs-string\">'My page'</span>)\n            -&gt;setVariable(<span class=\"hljs-string\">'date'</span>, now())\n            -&gt;setVariable(<span class=\"hljs-string\">'menu'</span>, [<span class=\"hljs-string\">'main'</span> =&gt; [<span class=\"hljs-string\">'weight'</span> =&gt; <span class=\"hljs-number\">99</span>]]);\n        <span class=\"hljs-keyword\">$this</span>-&gt;generatedPages-&gt;add($page);\n    }\n}</code></pre>\n<p><em>/extensions/Cecil/Generator/Database.php</em></p>\n<pre><code class=\"language-php hljs php\" translate=\"no\"><span class=\"hljs-meta\">&lt;?php</span>\n<span class=\"hljs-keyword\">namespace</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Generator</span>;\n\n<span class=\"hljs-keyword\">use</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Collection</span>\\<span class=\"hljs-title\">Page</span>\\<span class=\"hljs-title\">Page</span>;\n<span class=\"hljs-keyword\">use</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Collection</span>\\<span class=\"hljs-title\">Page</span>\\<span class=\"hljs-title\">Type</span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">Database</span> <span class=\"hljs-keyword\">extends</span> <span class=\"hljs-title\">AbstractGenerator</span> <span class=\"hljs-keyword\">implements</span> <span class=\"hljs-title\">GeneratorInterface</span>\n</span>{\n    <span class=\"hljs-keyword\">public</span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">generate</span><span class=\"hljs-params\">()</span>: <span class=\"hljs-title\">void</span>\n    </span>{\n        <span class=\"hljs-comment\">// create pages from a SQLite database</span>\n        $db = <span class=\"hljs-keyword\">new</span> SQLite3(<span class=\"hljs-string\">'database.sqlite'</span>);\n        $statement = $db-&gt;prepare(<span class=\"hljs-string\">'SELECT * FROM blog'</span>);\n        $result = $statement-&gt;execute();\n        <span class=\"hljs-keyword\">while</span> ($row = $result-&gt;fetchArray(SQLITE3_ASSOC)) {\n            $page = (<span class=\"hljs-keyword\">new</span> Page($row[<span class=\"hljs-string\">'page-id'</span>]))\n                -&gt;setType(Type::PAGE-&gt;value)\n                -&gt;setPath($row[<span class=\"hljs-string\">'path'</span>])\n                -&gt;setBodyHtml($row[<span class=\"hljs-string\">'html'</span>])\n                -&gt;setVariable(<span class=\"hljs-string\">'title'</span>, $row[<span class=\"hljs-string\">'title'</span>])\n                -&gt;setVariable(<span class=\"hljs-string\">'date'</span>, $row[<span class=\"hljs-string\">'date'</span>]);\n            <span class=\"hljs-keyword\">$this</span>-&gt;generatedPages-&gt;add($page);\n        }\n        $result-&gt;finalize();\n        $db-&gt;close();\n    }\n}</code></pre>\n<p><em>configuration</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">generators:</span>\n    <span class=\"hljs-comment\"># priority: class name</span>\n    <span class=\"hljs-attr\">99:</span> <span class=\"hljs-string\">Cecil\\Generator\\DummyPage</span>\n    <span class=\"hljs-attr\">35:</span> <span class=\"hljs-string\">Cecil\\Generator\\Database</span></code></pre>\n<h2 id=\"twig-extension\">Twig extension</h2>\n<p>You can add custom <a href=\"../templates/reference/1-functions.md\">functions</a> and <a href=\"../templates/reference/3-filters.md\">filters</a>:</p>\n<ol>\n<li><a href=\"https://twig.symfony.com/doc/advanced.html#creating-an-extension\" target=\"_blank\" rel=\"noopener noreferrer\">create a Twig extension</a> in the <code translate=\"no\">Cecil\\Renderer\\Extension</code> namespace</li>\n<li>add the PHP file in the <code translate=\"no\">extensions</code> directory</li>\n<li>add the class name to the configuration</li>\n</ol>\n<p><strong>Example:</strong></p>\n<p><em>/extensions/Cecil/Renderer/Extension/MyTwigExtension.php</em></p>\n<pre><code class=\"language-php hljs php\" translate=\"no\"><span class=\"hljs-meta\">&lt;?php</span>\n<span class=\"hljs-keyword\">namespace</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Renderer</span>\\<span class=\"hljs-title\">Extension</span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">MyTwigExtension</span> <span class=\"hljs-keyword\">extends</span> \\<span class=\"hljs-title\">Twig</span>\\<span class=\"hljs-title\">Extension</span>\\<span class=\"hljs-title\">AbstractExtension</span>\n</span>{\n    <span class=\"hljs-keyword\">public</span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">getFilters</span><span class=\"hljs-params\">()</span>\n    </span>{\n        <span class=\"hljs-comment\">// add a new filter named 'md5'</span>\n        <span class=\"hljs-keyword\">return</span> [\n            <span class=\"hljs-keyword\">new</span> \\Twig\\TwigFilter(<span class=\"hljs-string\">'md5'</span>, <span class=\"hljs-string\">'md5'</span>),\n        ];\n    }\n}</code></pre>\n<p><em>configuration</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">layouts:</span>\n  <span class=\"hljs-attr\">extensions:</span>\n    <span class=\"hljs-attr\">MyExtension:</span> <span class=\"hljs-string\">Cecil\\Renderer\\Extension\\MyTwigExtension</span></code></pre>\n<h2 id=\"output-post-processor\">Output Post Processor</h2>\n<p>You can post process page output.</p>\n<p>Just create a new PHP class in the <code translate=\"no\">Cecil\\Renderer\\PostProcessor</code> namespace and add the class name to the `output.postprocessors list.</p>\n<p><strong>Example:</strong></p>\n<p><em>/extensions/Cecil/Renderer/PostProcessor/MyProcessor.php</em></p>\n<pre><code class=\"language-php hljs php\" translate=\"no\"><span class=\"hljs-meta\">&lt;?php</span>\n<span class=\"hljs-keyword\">namespace</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Renderer</span>\\<span class=\"hljs-title\">PostProcessor</span>;\n\n<span class=\"hljs-keyword\">use</span> <span class=\"hljs-title\">Cecil</span>\\<span class=\"hljs-title\">Collection</span>\\<span class=\"hljs-title\">Page</span>\\<span class=\"hljs-title\">Page</span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">MyProcessor</span> <span class=\"hljs-keyword\">extends</span> <span class=\"hljs-title\">AbstractPostProcessor</span>\n</span>{\n    <span class=\"hljs-keyword\">public</span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-title\">process</span><span class=\"hljs-params\">(Page $page, string $output, string $format)</span>: <span class=\"hljs-title\">string</span>\n    </span>{\n        <span class=\"hljs-comment\">// add a meta tag to the head of the HTML output</span>\n        <span class=\"hljs-keyword\">if</span> ($format == <span class=\"hljs-string\">'html'</span>) {\n            <span class=\"hljs-keyword\">if</span> (!preg_match(<span class=\"hljs-string\">'/&lt;meta name=\"test\".*/i'</span>, $output)) {\n                $meta = \\sprintf(<span class=\"hljs-string\">'&lt;meta name=\"test\" content=\"Test\"&gt;'</span>);\n                $output = preg_replace_callback(<span class=\"hljs-string\">'/([[:blank:]]*)(&lt;\\/head&gt;)/i'</span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span> <span class=\"hljs-params\">($matches)</span> <span class=\"hljs-title\">use</span> <span class=\"hljs-params\">($meta)</span> </span>{\n                    <span class=\"hljs-keyword\">return</span> str_repeat($matches[<span class=\"hljs-number\">1</span>] ?: <span class=\"hljs-string\">' '</span>, <span class=\"hljs-number\">2</span>) . $meta . <span class=\"hljs-string\">\"\\n\"</span> . $matches[<span class=\"hljs-number\">1</span>] . $matches[<span class=\"hljs-number\">2</span>];\n                }, $output);\n            }\n        }\n\n        <span class=\"hljs-keyword\">return</span> $output;\n    }\n}</code></pre>\n<p><em>configuration</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">output:</span>\n  <span class=\"hljs-attr\">postprocessors:</span>\n    <span class=\"hljs-attr\">MyProcessor:</span> <span class=\"hljs-string\">Cecil\\Renderer\\PostProcessor\\MyProcessor</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/assets/images/",
      "url": "https://cecil-945.pages.dev/documentation/assets/images/",
      "title": "Images",
      "summary": "Resize, crop and convert images, generate responsive images and placeholders.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Images\nCecil provides Twig functions and filters to resize, crop and convert images, and to generate responsive images and placeholders.\nimage_srcset\nBuilds the HTML img srcset (responsive) attribute of an image Asset.\n{{ image_srcset(asset) }}\nExamples:\n{% set asset = asset(image_path) %}\n&lt;img src=\"{{ url(asset) }}\" width=\"{{ asset.width }}\" height=\"{{ asset.height }}\" alt=\"\" class=\"asset\" srcset=\"{{ image_srcset(asset) }}\" sizes=\"{{ image_sizes('asset') }}\"&gt;\nimage_sizes\nReturns the HTML img sizes attribute based on a CSS class name.\nIt should be use in conjunction with the image_srcset function.\n{{ image_sizes('class') }}\nExamples:\n{% set asset = asset(image_path) %}\n&lt;img src=\"{{ url(asset) }}\" width=\"{{ asset.width }}\" height=\"{{ asset.height }}\" alt=\"\" class=\"asset\" srcset=\"{{ image_srcset(asset) }}\" sizes=\"{{ image_sizes('asset') }}\"&gt;\nimage_from_website\nBuilds the HTML img element from a website URL by extracting its illustration image.\nReturns null if no image is found.\n{{ image_from_website('url', {attributes}, {options}) }}\nThe image is searched in the page HTML with the following fallbacks, the first candidate that can be downloaded as an image is used:\n\nOpen Graph: og:image:secure_url, og:image, og:image:url\nTwitter: twitter:image, twitter:image:src\n&lt;link rel=\"image_src\"&gt;\nMicrodata: itemprop=\"image\"\nJSON-LD: image property\nFirst &lt;img&gt; of &lt;article&gt;, &lt;main&gt; or &lt;body&gt;\n&lt;link rel=\"apple-touch-icon\"&gt;\n&lt;link rel=\"icon\"&gt;\n\nRelative URLs are resolved against &lt;base href&gt; or the page URL.\nThe resolved image URL and the downloaded image are cached (see cache.assets.remote.ttl).\nOptions:\n\nfallback: image path (or URL) used if no image is found\nother image options (e.g.: responsive, formats)\n\nExamples:\n{{ image_from_website('https:\/\/example.com\/page-with-image.html') }}\n\n{# with a fallback image #}\n{{ image_from_website('https:\/\/example.com\/', {alt: 'Illustration'}, {fallback: 'images\/default.png'}) }}\nresize\nResizes an image to a specified width (in pixels) or\/and height (in pixels).\n\nIf only the width is specified, the height is calculated to preserve the aspect ratio\nIf only the height is specified, the width is calculated to preserve the aspect ratio\nIf both width and height are specified, the image is resized to fit within the given dimensions, image is cropped and centered if necessary\nIf remove_animation is true, any animation in the image (e.g., GIF) will be removed\n\n{{ asset(image_path)|resize(width: width, height: height, remove_animation: bool) }}\nThe original file is not altered and the resized version is saved at \/thumbnails\/&lt;width&gt;x&lt;height&gt;\/image.jpg.\nICO files are supported: the largest icon is resized and saved as a single icon ICO file (PNG compressed). Icons stored as BMP with a color depth other than 24 or 32 bits require the Imagick PHP extension: otherwise, the original ICO file is kept and a warning is logged.\nExamples:\n{{ asset(page.image)|resize(300) }}\n{# equivalent to: #}\n{{ asset(page.image)|resize(width: 300) }}\n{# resizes to 300px width, height auto-calculated to preserve aspect ratio #}\n{{ asset(page.image)|resize(height: 200) }}\n{# resizes to 300px width and 200px height, and crops if necessary #}\n{{ asset(page.image)|resize(300, 200) }}\n{# removes any animation from the image #}\n{{ asset(page.image)|resize(width: 1200, height: 630, remove_animation: true) }}\ncover\nResizes an image to a specified width and height, cropping it if necessary.\nThe cover filter is deprecated since version 8.77 and will be removed in future versions. Use the resize filter instead, with both width and height parameters.\n{{ asset(image_path)|cover(width, height) }}\nExample:\n{{ asset(page.image)|cover(1200, 630) }}\nmaskable\nAdds padding, in pourcentages, to an image to make it maskable.\n{{ asset(image_path)|maskable(padding) }}\nExample:\n{{ asset('icon.png')|maskable }}\nwebp\nConverts an image to WebP format.\nExample:\n&lt;picture&gt;\n    &lt;source type=\"image\/webp\" srcset=\"{{ asset(image_path)|webp }}\"&gt;\n    &lt;img src=\"{{ url(asset(image_path)) }}\" width=\"{{ asset(image_path).width }}\" height=\"{{ asset(image_path).height }}\" alt=\"\"&gt;\n&lt;\/picture&gt;\navif\nConverts an image to AVIF format.\nExample:\n&lt;picture&gt;\n    &lt;source type=\"image\/avif\" srcset=\"{{ asset(image_path)|avif }}\"&gt;\n    &lt;img src=\"{{ url(asset(image_path)) }}\" width=\"{{ asset(image_path).width }}\" height=\"{{ asset(image_path).height }}\" alt=\"\"&gt;\n&lt;\/picture&gt;\nlqip\nReturns a Low Quality Image Placeholder (100x100 px, 50% blurred) as data URL.\n{{ asset(image_path)|lqip }}\ndominant_color\nReturns the dominant hexadecimal color of an image.\n{{ asset(image_path)|dominant_color }}",
      "content_html": "<h1>Images</h1>\n<p>Cecil provides Twig functions and filters to resize, crop and convert images, and to generate responsive images and placeholders.</p>\n<h2 id=\"image-srcset\">image_srcset</h2>\n<p>Builds the HTML img <code translate=\"no\">srcset</code> (responsive) attribute of an image Asset.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ image_srcset(asset) }}</span></code></pre>\n<p><em>Examples:</em></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> asset = asset(image_path) %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ url(asset) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset.width }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset.height }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">alt</span>=<span class=\"hljs-string\">\"\"</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"asset\"</span> <span class=\"hljs-attr\">srcset</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ image_srcset(asset) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">sizes</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ image_sizes('asset') }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span></code></pre>\n<h2 id=\"image-sizes\">image_sizes</h2>\n<p>Returns the HTML img <code translate=\"no\">sizes</code> attribute based on a CSS class name.<br>\nIt should be use in conjunction with the <a href=\"1-images.md#image-srcset\"><code translate=\"no\">image_srcset</code></a> function.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ image_sizes('class') }}</span></code></pre>\n<p><em>Examples:</em></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> asset = asset(image_path) %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ url(asset) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset.width }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset.height }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">alt</span>=<span class=\"hljs-string\">\"\"</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"asset\"</span> <span class=\"hljs-attr\">srcset</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ image_srcset(asset) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">sizes</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ image_sizes('asset') }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span></code></pre>\n<h2 id=\"image-from-website\">image_from_website</h2>\n<p>Builds the HTML img element from a website URL by extracting its illustration image.\nReturns <code translate=\"no\">null</code> if no image is found.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ image_from_website('url', {attributes}, {options}) }}</span></code></pre>\n<p>The image is searched in the page HTML with the following fallbacks, the first candidate that can be downloaded as an image is used:</p>\n<ol>\n<li>Open Graph: <code translate=\"no\">og:image:secure_url</code>, <code translate=\"no\">og:image</code>, <code translate=\"no\">og:image:url</code></li>\n<li>Twitter: <code translate=\"no\">twitter:image</code>, <code translate=\"no\">twitter:image:src</code></li>\n<li><code translate=\"no\">&lt;link rel=\"image_src\"&gt;</code></li>\n<li>Microdata: <code translate=\"no\">itemprop=\"image\"</code></li>\n<li>JSON-LD: <code translate=\"no\">image</code> property</li>\n<li>First <code translate=\"no\">&lt;img&gt;</code> of <code translate=\"no\">&lt;article&gt;</code>, <code translate=\"no\">&lt;main&gt;</code> or <code translate=\"no\">&lt;body&gt;</code></li>\n<li><code translate=\"no\">&lt;link rel=\"apple-touch-icon\"&gt;</code></li>\n<li><code translate=\"no\">&lt;link rel=\"icon\"&gt;</code></li>\n</ol>\n<p>Relative URLs are resolved against <code translate=\"no\">&lt;base href&gt;</code> or the page URL.</p>\n<p>The resolved image URL and the downloaded image are cached (see <a href=\"../configuration/9-cache.md\"><code translate=\"no\">cache.assets.remote.ttl</code></a>).</p>\n<p>Options:</p>\n<ul>\n<li><code translate=\"no\">fallback</code>: image path (or URL) used if no image is found</li>\n<li>other <a href=\"../templates/reference/1-functions.md#html\"><code translate=\"no\">image</code></a> options (e.g.: <code translate=\"no\">responsive</code>, <code translate=\"no\">formats</code>)</li>\n</ul>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ image_from_website('https://example.com/page-with-image.html') }}</span><span class=\"xml\">\n\n</span><span class=\"hljs-comment\">{# with a fallback image #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ image_from_website('https://example.com/', {alt: 'Illustration'}, {fallback: 'images/default.png'}) }}</span></code></pre>\n<h2 id=\"resize\">resize</h2>\n<p>Resizes an image to a specified width (in pixels) or/and height (in pixels).</p>\n<ul>\n<li>If only the width is specified, the height is calculated to preserve the aspect ratio</li>\n<li>If only the height is specified, the width is calculated to preserve the aspect ratio</li>\n<li>If both width and height are specified, the image is resized to fit within the given dimensions, image is cropped and centered if necessary</li>\n<li>If remove_animation is true, any animation in the image (e.g., GIF) will be removed</li>\n</ul>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(image_path)|resize(width: width, height: height, remove_animation: bool) }}</span></code></pre>\n<aside class=\"note note-info\"><p>The original file is not altered and the resized version is saved at <code translate=\"no\">/thumbnails/&lt;width&gt;x&lt;height&gt;/image.jpg</code>.</p></aside>\n<aside class=\"note note-tip\"><p>ICO files are supported: the largest icon is resized and saved as a single icon ICO file (PNG compressed). Icons stored as BMP with a color depth other than 24 or 32 bits require the <a href=\"https://www.php.net/manual/book.imagick.php\" target=\"_blank\" rel=\"noopener noreferrer\">Imagick</a> PHP extension: otherwise, the original ICO file is kept and a warning is logged.</p></aside>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(page.image)|resize(300) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# equivalent to: #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ asset(page.image)|resize(width: 300) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# resizes to 300px width, height auto-calculated to preserve aspect ratio #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ asset(page.image)|resize(height: 200) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# resizes to 300px width and 200px height, and crops if necessary #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ asset(page.image)|resize(300, 200) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# removes any animation from the image #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ asset(page.image)|resize(width: 1200, height: 630, remove_animation: true) }}</span></code></pre>\n<h2 id=\"cover\">cover</h2>\n<p>Resizes an image to a specified width and height, cropping it if necessary.</p>\n<aside class=\"note note-warning\"><p>The <code translate=\"no\">cover</code> filter is deprecated since version <ins>8.77</ins> and will be removed in future versions. Use the <a href=\"#resize\"><code translate=\"no\">resize</code></a> filter instead, with both width and height parameters.</p></aside>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(image_path)|cover(width, height) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(page.image)|cover(1200, 630) }}</span></code></pre>\n<h2 id=\"maskable\">maskable</h2>\n<p>Adds padding, in pourcentages, to an image to make it maskable.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(image_path)|maskable(padding) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset('icon.png')|maskable }}</span></code></pre>\n<h2 id=\"webp\">webp</h2>\n<p>Converts an image to <a href=\"https://developers.google.com/speed/webp\" target=\"_blank\" rel=\"noopener noreferrer\">WebP</a> format.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">picture</span>&gt;</span>\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\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset(image_path)|webp }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ url(asset(image_path)) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset(image_path).width }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset(image_path).height }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">alt</span>=<span class=\"hljs-string\">\"\"</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">picture</span>&gt;</span></span></code></pre>\n<h2 id=\"avif\">avif</h2>\n<p>Converts an image to <a href=\"https://github.com/AOMediaCodec/libavif\" target=\"_blank\" rel=\"noopener noreferrer\">AVIF</a> format.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">picture</span>&gt;</span>\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\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset(image_path)|avif }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ url(asset(image_path)) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset(image_path).width }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ asset(image_path).height }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">alt</span>=<span class=\"hljs-string\">\"\"</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">picture</span>&gt;</span></span></code></pre>\n<h2 id=\"lqip\">lqip</h2>\n<p>Returns a <a href=\"https://www.guypo.com/introducing-lqip-low-quality-image-placeholders\" target=\"_blank\" rel=\"noopener noreferrer\">Low Quality Image Placeholder</a> (100x100 px, 50% blurred) as data URL.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(image_path)|lqip }}</span></code></pre>\n<h2 id=\"dominant-color\">dominant_color</h2>\n<p>Returns the dominant <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color\" target=\"_blank\" rel=\"noopener noreferrer\">hexadecimal color</a> of an image.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(image_path)|dominant_color }}</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/site/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/site/",
      "title": "Site options",
      "summary": "title, baseurl, menus, taxonomies, theme, date, metatags, debug, etc.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Site options\nThese options define the main settings of the site: title, URL, description, menus, taxonomies, theme, metatags, etc.\ntitle\nMain title of the site.\ntitle: \"&lt;site title&gt;\"\nbaseline\nShort description (~ 20 characters).\nbaseline: \"&lt;baseline&gt;\"\nbaseurl\nThe base URL.\nbaseurl: &lt;url&gt;\nExample:\nbaseurl: http:\/\/localhost:8000\/\nbaseurl should end with a trailing slash (\/).\ncanonicalurl\nIf set to true the url() function will return the absolute URL (false by default).\ncanonicalurl: &lt;true|false&gt; # false by default\ndescription\nSite description (~ 250 characters).\ndescription: \"&lt;description&gt;\"\nmenus\nMenus are used to create navigation links in templates.\nA menu is made up of a unique ID and entry properties (name, URL, weight).\nmenus:\n  &lt;name&gt;:\n    - id: &lt;unique-id&gt;   # unique identifier (required)\n      name: \"&lt;name&gt;\"    # name displayed in templates\n      url: &lt;url&gt;        # relative or absolute URL\n      weight: &lt;integer&gt; # integer value used to sort entries (lighter first)\nExample:\nmenus:\n  main:\n    - id: about\n      name: \"About\"\n      url: \/about\/\n      weight: 1\n  footer:\n    - id: author\n      name: The author\n      url: https:\/\/arnaudligny.fr\n      weight: 99\nA main menu is automatically created with the home page entry and all sections entries (See content management)\nA page can be added to a menu by setting the menu variable in its front matter.\nOverride an entry\nA page menu entry can be overridden: use the page ID as id.\nExample:\nmenus:\n  main:\n    - id: index\n      name: \"My amazing homepage!\"\n      weight: 1\nDisable an entry\nA menu entry can be disabled with enabled: false.\nExample:\nmenus:\n  main:\n    - id: about\n      enabled: false\ntaxonomies\nList of vocabularies, paired by plural and singular value.\ntaxonomies:\n  &lt;plural&gt;: &lt;singular&gt;\nExample:\ntaxonomies:\n  categories: category\n  tags: tag\nThen you can use those vocabularies in your content’s front matter.\nSince version 8.37.0, default vocabularies category and tag have been removed. You must define them in the configuration file if you want to use them.\nA vocabulary can be disabled with the special value disabled. Example: tags: disabled.\ntheme\nThe theme to use, or a list of themes.\ntheme: &lt;theme&gt; # theme name\n# or\ntheme:\n  - &lt;theme1&gt; # theme name\n  - &lt;theme2&gt;\nThe first theme overrides the others, and so on.\nExamples:\ntheme: hyde\ntheme:\n  - serviceworker\n  - hyde\nSee themes on GitHub or on website themes section.\ndate\nDate format and timezone.\ndate:\n  format: &lt;format&gt;     # date format (optional, `F j, Y` by default)\n  timezone: &lt;timezone&gt; # date timezone (optional, local time zone by default)\n\nformat: PHP date format specifier\ntimezone: see timezones\n\nExample:\ndate:\n  format: 'j F, Y'\n  timezone: 'Europe\/Paris'\nmetatags\nmetatags are SEO and social helpers that can be automatically injected in the &lt;head&gt;, with the template partials\/metatags.html.twig.\nThis template adds the following meta tags:\n\nPage title + Site title, or Site title + Site baseline\nPage\/Site description\nPage\/Site keywords\nPage\/Site author\nSearch engine crawler directives (robots)\nFavicon links\nNavigation links (first, previous, next, last)\nCanonical URL\nAlternate links (i.e.: RSS feed, others languages)\nrel=me links\nOpen Graph\nFacebook profile ID\nTwitter\/X Card\nFediverse tag\nDublin Core\nStructured data (JSON-LD)\n\nmetatags options\nCecil uses page front matter to feed meta tags, and falls back to site options when needed.\ntitle: \"Page\/Site title\"              # used by title meta\ndescription: \"Page\/Site description\"  # used by description meta\ntags: [tag1, tag2]                    # used by keywords meta\nkeywords: [keyword1, keyword2]        # obsolete\nauthor:                               # used by author meta\n  name: &lt;name&gt;                          # author name\n  url: &lt;url&gt;                            # author URL\n  email: &lt;email&gt;                        # author email\nimage: image.jpg                      # used by Open Graph and social networks cards\ncanonical:                            # used to override the generated canonical URL\n  url: &lt;URL&gt;                            # absolute URL\n  title: \"&lt;URL title&gt;\"                  # optional canonical title\nsocial:                               # used by social networks meta\n  twitter:                              # used by Twitter\/X Card\n    url: &lt;URL&gt;                            # used for `rel=me` link\n    site: username                        # site username\n    creator: username                     # page author username\n  mastodon:                             # used by Mastodon meta\n    url: &lt;URL&gt;                            # used for `rel=me` link\n    creator: handle                       # page author account\n  facebook:                             # used by Facebook meta\n    url: &lt;URL&gt;                            # used for `rel=me` link\n    id: 123456789                         # Facebook profile ID\n    username: username                    # page author username\nIf needed, title and image can be overridden:\n{{ include('partials\/metatags.html.twig', {title: 'Custom title', image: og_image}) }}\nmetatags configuration\nmetatags:\n  title:                   # title options\n    divider: \" &amp;middot; \"    # string between page title and site title\n    only: false              # displays page title only (`false` by default)\n    pagination:              # pagination options\n      shownumber: true         # displays page number in title (`true` by default)\n      label: \"Page %s\"         # how to display page number (`Page %s` by default)\n  robots: \"index,follow\"   # web crawlers directives (`index,follow` by default)\n  favicon:                 # favicon options\n    enabled: true            # includes favicon (`true` by default)\n    image: favicon.png       # path to favicon image\n    sizes:                   # sizes by device\n      - \"icon\": [32, 57, 76, 96, 128, 192, 228]  # web browsers\n      - \"shortcut icon\": [196]                   # Android\n      - \"apple-touch-icon\": [120, 152, 180]      # iOS\n  navigation: true         # includes previous and next links (`true` by default)\n  image: true              # includes image (`true` by default)\n  og: true                 # includes Open Graph meta tags (`true` by default)\n  articles: \"blog\"         # articles' section (`blog` by default)\n  twitter: true            # includes Twitter\/X Card meta tags (`true` by default)\n  mastodon: true           # includes Mastodon meta tags (`true` by default)\n  dc: false                # includes Dublin Core meta tags (`false` by default)\n  data: false              # includes JSON-LD structured data (`false` by default)\ndebug\nEnables the debug mode, used to display debug information like very verbose logs, Twig dump, Twig profiler, SCSS sourcemap, etc.\ndebug: true\nThere are two other ways to enable debug mode:\n\nRun a command with the -vvv option\nSet the CECIL_DEBUG environment variable to true\n\n",
      "content_html": "<h1>Site options</h1>\n<p>These options define the main settings of the site: title, URL, description, menus, taxonomies, theme, metatags, etc.</p>\n<h2 id=\"title\">title</h2>\n<p>Main title of the site.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">\"&lt;site title&gt;\"</span></code></pre>\n<h2 id=\"baseline\">baseline</h2>\n<p>Short description (~ 20 characters).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">baseline:</span> <span class=\"hljs-string\">\"&lt;baseline&gt;\"</span></code></pre>\n<h2 id=\"baseurl\">baseurl</h2>\n<p>The base URL.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">baseurl:</span> <span class=\"hljs-string\">&lt;url&gt;</span></code></pre>\n<p><em>Example:</em></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></code></pre>\n<aside class=\"note note-important\"><p><code translate=\"no\">baseurl</code> should end with a trailing slash (<code translate=\"no\">/</code>).</p></aside>\n<h2 id=\"canonicalurl\">canonicalurl</h2>\n<p>If set to <code translate=\"no\">true</code> the <a href=\"../templates/reference/1-functions.md#url\"><code translate=\"no\">url()</code></a> function will return the absolute URL (<code translate=\"no\">false</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">canonicalurl:</span> <span class=\"hljs-string\">&lt;true|false&gt;</span> <span class=\"hljs-comment\"># false by default</span></code></pre>\n<h2 id=\"description\">description</h2>\n<p>Site description (~ 250 characters).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">description:</span> <span class=\"hljs-string\">\"&lt;description&gt;\"</span></code></pre>\n<h2 id=\"menus\">menus</h2>\n<p>Menus are used to create <a href=\"../templates/2-variables.md#site-menus\">navigation links in templates</a>.</p>\n<p>A menu is made up of a unique ID and entry properties (name, URL, weight).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">menus:</span>\n  <span class=\"hljs-string\">&lt;name&gt;:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">id:</span> <span class=\"hljs-string\">&lt;unique-id&gt;</span>   <span class=\"hljs-comment\"># unique identifier (required)</span>\n      <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">\"&lt;name&gt;\"</span>    <span class=\"hljs-comment\"># name displayed in templates</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">&lt;url&gt;</span>        <span class=\"hljs-comment\"># relative or absolute URL</span>\n      <span class=\"hljs-attr\">weight:</span> <span class=\"hljs-string\">&lt;integer&gt;</span> <span class=\"hljs-comment\"># integer value used to sort entries (lighter first)</span></code></pre>\n<p><em>Example:</em></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\">author</span>\n      <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">The</span> <span class=\"hljs-string\">author</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">https://arnaudligny.fr</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 (<a href=\"../content/index.md\">See content management</a>)</p></aside>\n<aside class=\"note note-tip\"><p>A page can be added to a menu by setting the <a href=\"../content/2-front-matter.md#menu\"><code translate=\"no\">menu</code> variable</a> in its front matter.</p></aside>\n<h3 id=\"override-an-entry\">Override an entry</h3>\n<p>A page menu entry can be overridden: use the page ID as <code translate=\"no\">id</code>.</p>\n<p><em>Example:</em></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\">\"My amazing homepage!\"</span>\n      <span class=\"hljs-attr\">weight:</span> <span class=\"hljs-number\">1</span></code></pre>\n<h3 id=\"disable-an-entry\">Disable an entry</h3>\n<p>A menu entry can be disabled with <code translate=\"no\">enabled: false</code>.</p>\n<p><em>Example:</em></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\">enabled:</span> <span class=\"hljs-literal\">false</span></code></pre>\n<h2 id=\"taxonomies\">taxonomies</h2>\n<p>List of vocabularies, paired by plural and singular value.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">taxonomies:</span>\n  <span class=\"hljs-string\">&lt;plural&gt;:</span> <span class=\"hljs-string\">&lt;singular&gt;</span></code></pre>\n<p><em>Example:</em></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<p>Then you can use those vocabularies in your content’s <a href=\"../content/2-front-matter.md#taxonomy\">front matter</a>.</p>\n<aside class=\"note note-warning\"><p>Since <ins>version 8.37.0</ins>, default vocabularies <code translate=\"no\">category</code> and <code translate=\"no\">tag</code> have been removed. You must define them in the configuration file if you want to use them.</p></aside>\n<aside class=\"note note-tip\"><p>A vocabulary can be disabled with the special value <code translate=\"no\">disabled</code>. Example: <code translate=\"no\">tags: disabled</code>.</p></aside>\n<h2 id=\"theme\">theme</h2>\n<p>The theme to use, or a list of themes.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">theme:</span> <span class=\"hljs-string\">&lt;theme&gt;</span> <span class=\"hljs-comment\"># theme name</span>\n<span class=\"hljs-comment\"># or</span>\n<span class=\"hljs-attr\">theme:</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">&lt;theme1&gt;</span> <span class=\"hljs-comment\"># theme name</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">&lt;theme2&gt;</span></code></pre>\n<aside class=\"note note-info\"><p>The first theme overrides the others, and so on.</p></aside>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">theme:</span> <span class=\"hljs-string\">hyde</span></code></pre>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">theme:</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">serviceworker</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">hyde</span></code></pre>\n<aside class=\"note note-info\"><p>See <a href=\"https://github.com/Cecilapp?q=theme#org-repositories\" target=\"_blank\" rel=\"noopener noreferrer\">themes on GitHub</a> or on website <a href=\"https://cecil.app/themes/\" target=\"_blank\" rel=\"noopener noreferrer\">themes section</a>.</p></aside>\n<h2 id=\"date\">date</h2>\n<p>Date format and timezone.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">date:</span>\n  <span class=\"hljs-attr\">format:</span> <span class=\"hljs-string\">&lt;format&gt;</span>     <span class=\"hljs-comment\"># date format (optional, `F j, Y` by default)</span>\n  <span class=\"hljs-attr\">timezone:</span> <span class=\"hljs-string\">&lt;timezone&gt;</span> <span class=\"hljs-comment\"># date timezone (optional, local time zone by default)</span></code></pre>\n<ul>\n<li><code translate=\"no\">format</code>: <a href=\"https://php.net/date\" target=\"_blank\" rel=\"noopener noreferrer\">PHP date</a> format specifier</li>\n<li><code translate=\"no\">timezone</code>: see <a href=\"https://php.net/timezones\" target=\"_blank\" rel=\"noopener noreferrer\">timezones</a></li>\n</ul>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">date:</span>\n  <span class=\"hljs-attr\">format:</span> <span class=\"hljs-string\">'j F, Y'</span>\n  <span class=\"hljs-attr\">timezone:</span> <span class=\"hljs-string\">'Europe/Paris'</span></code></pre>\n<h2 id=\"metatags\">metatags</h2>\n<p><em>metatags</em> are SEO and social helpers that can be automatically injected in the <code translate=\"no\">&lt;head&gt;</code>, with the template <a href=\"https://github.com/Cecilapp/Cecil/blob/main/resources/layouts/partials/metatags.html.twig\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">partials/metatags.html.twig</code></a>.</p>\n<p>This template adds the following meta tags:</p>\n<ul>\n<li>Page title + Site title, or Site title + Site baseline</li>\n<li>Page/Site description</li>\n<li>Page/Site keywords</li>\n<li>Page/Site author</li>\n<li>Search engine crawler directives (<em>robots</em>)</li>\n<li>Favicon links</li>\n<li>Navigation links (first, previous, next, last)</li>\n<li>Canonical URL</li>\n<li>Alternate links (i.e.: RSS feed, others languages)</li>\n<li><a href=\"https://developer.mozilla.org/docs/Web/HTML/Reference/Attributes/rel/me\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">rel=me</code></a> links</li>\n<li><a href=\"https://ogp.me\" target=\"_blank\" rel=\"noopener noreferrer\">Open Graph</a></li>\n<li>Facebook profile ID</li>\n<li><a href=\"https://developer.x.com/docs/x-for-websites/cards/guides/getting-started\" target=\"_blank\" rel=\"noopener noreferrer\">Twitter/X Card</a></li>\n<li><a href=\"https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/\" target=\"_blank\" rel=\"noopener noreferrer\">Fediverse tag</a></li>\n<li><a href=\"https://www.dublincore.org/specifications/dublin-core/dcmi-terms/\" target=\"_blank\" rel=\"noopener noreferrer\">Dublin Core</a></li>\n<li><a href=\"https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data\" target=\"_blank\" rel=\"noopener noreferrer\">Structured data</a> (JSON-LD)</li>\n</ul>\n<h3 id=\"metatags-options\">metatags options</h3>\n<p>Cecil uses page front matter to feed meta tags, and falls back to site options when needed.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">\"Page/Site title\"</span>              <span class=\"hljs-comment\"># used by title meta</span>\n<span class=\"hljs-attr\">description:</span> <span class=\"hljs-string\">\"Page/Site description\"</span>  <span class=\"hljs-comment\"># used by description meta</span>\n<span class=\"hljs-attr\">tags:</span> <span class=\"hljs-string\">[tag1,</span> <span class=\"hljs-string\">tag2]</span>                    <span class=\"hljs-comment\"># used by keywords meta</span>\n<span class=\"hljs-attr\">keywords:</span> <span class=\"hljs-string\">[keyword1,</span> <span class=\"hljs-string\">keyword2]</span>        <span class=\"hljs-comment\"># obsolete</span>\n<span class=\"hljs-attr\">author:</span>                               <span class=\"hljs-comment\"># used by author meta</span>\n  <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">&lt;name&gt;</span>                          <span class=\"hljs-comment\"># author name</span>\n  <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">&lt;url&gt;</span>                            <span class=\"hljs-comment\"># author URL</span>\n  <span class=\"hljs-attr\">email:</span> <span class=\"hljs-string\">&lt;email&gt;</span>                        <span class=\"hljs-comment\"># author email</span>\n<span class=\"hljs-attr\">image:</span> <span class=\"hljs-string\">image.jpg</span>                      <span class=\"hljs-comment\"># used by Open Graph and social networks cards</span>\n<span class=\"hljs-attr\">canonical:</span>                            <span class=\"hljs-comment\"># used to override the generated canonical URL</span>\n  <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">&lt;URL&gt;</span>                            <span class=\"hljs-comment\"># absolute URL</span>\n  <span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">\"&lt;URL title&gt;\"</span>                  <span class=\"hljs-comment\"># optional canonical title</span>\n<span class=\"hljs-attr\">social:</span>                               <span class=\"hljs-comment\"># used by social networks meta</span>\n  <span class=\"hljs-attr\">twitter:</span>                              <span class=\"hljs-comment\"># used by Twitter/X Card</span>\n    <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">&lt;URL&gt;</span>                            <span class=\"hljs-comment\"># used for `rel=me` link</span>\n    <span class=\"hljs-attr\">site:</span> <span class=\"hljs-string\">username</span>                        <span class=\"hljs-comment\"># site username</span>\n    <span class=\"hljs-attr\">creator:</span> <span class=\"hljs-string\">username</span>                     <span class=\"hljs-comment\"># page author username</span>\n  <span class=\"hljs-attr\">mastodon:</span>                             <span class=\"hljs-comment\"># used by Mastodon meta</span>\n    <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">&lt;URL&gt;</span>                            <span class=\"hljs-comment\"># used for `rel=me` link</span>\n    <span class=\"hljs-attr\">creator:</span> <span class=\"hljs-string\">handle</span>                       <span class=\"hljs-comment\"># page author account</span>\n  <span class=\"hljs-attr\">facebook:</span>                             <span class=\"hljs-comment\"># used by Facebook meta</span>\n    <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">&lt;URL&gt;</span>                            <span class=\"hljs-comment\"># used for `rel=me` link</span>\n    <span class=\"hljs-attr\">id:</span> <span class=\"hljs-number\">123456789</span>                         <span class=\"hljs-comment\"># Facebook profile ID</span>\n    <span class=\"hljs-attr\">username:</span> <span class=\"hljs-string\">username</span>                    <span class=\"hljs-comment\"># page author username</span></code></pre>\n<aside class=\"note note-tip\"><p>If needed, <code translate=\"no\">title</code> and <code translate=\"no\">image</code> can be overridden:</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></aside>\n<h3 id=\"metatags-configuration\">metatags configuration</h3>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">metatags:</span>\n  <span class=\"hljs-attr\">title:</span>                   <span class=\"hljs-comment\"># title options</span>\n    <span class=\"hljs-attr\">divider:</span> <span class=\"hljs-string\">\" &amp;middot; \"</span>    <span class=\"hljs-comment\"># string between page title and site title</span>\n    <span class=\"hljs-attr\">only:</span> <span class=\"hljs-literal\">false</span>              <span class=\"hljs-comment\"># displays page title only (`false` by default)</span>\n    <span class=\"hljs-attr\">pagination:</span>              <span class=\"hljs-comment\"># pagination options</span>\n      <span class=\"hljs-attr\">shownumber:</span> <span class=\"hljs-literal\">true</span>         <span class=\"hljs-comment\"># displays page number in title (`true` by default)</span>\n      <span class=\"hljs-attr\">label:</span> <span class=\"hljs-string\">\"Page %s\"</span>         <span class=\"hljs-comment\"># how to display page number (`Page %s` by default)</span>\n  <span class=\"hljs-attr\">robots:</span> <span class=\"hljs-string\">\"index,follow\"</span>   <span class=\"hljs-comment\"># web crawlers directives (`index,follow` by default)</span>\n  <span class=\"hljs-attr\">favicon:</span>                 <span class=\"hljs-comment\"># favicon options</span>\n    <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>            <span class=\"hljs-comment\"># includes favicon (`true` by default)</span>\n    <span class=\"hljs-attr\">image:</span> <span class=\"hljs-string\">favicon.png</span>       <span class=\"hljs-comment\"># path to favicon image</span>\n    <span class=\"hljs-attr\">sizes:</span>                   <span class=\"hljs-comment\"># sizes by device</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">\"icon\":</span> <span class=\"hljs-string\">[32,</span> <span class=\"hljs-number\">57</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">76</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">96</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">128</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">192</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">228</span><span class=\"hljs-string\">]</span>  <span class=\"hljs-comment\"># web browsers</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">\"shortcut icon\":</span> <span class=\"hljs-string\">[196]</span>                   <span class=\"hljs-comment\"># Android</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">\"apple-touch-icon\":</span> <span class=\"hljs-string\">[120,</span> <span class=\"hljs-number\">152</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">180</span><span class=\"hljs-string\">]</span>      <span class=\"hljs-comment\"># iOS</span>\n  <span class=\"hljs-attr\">navigation:</span> <span class=\"hljs-literal\">true</span>         <span class=\"hljs-comment\"># includes previous and next links (`true` by default)</span>\n  <span class=\"hljs-attr\">image:</span> <span class=\"hljs-literal\">true</span>              <span class=\"hljs-comment\"># includes image (`true` by default)</span>\n  <span class=\"hljs-attr\">og:</span> <span class=\"hljs-literal\">true</span>                 <span class=\"hljs-comment\"># includes Open Graph meta tags (`true` by default)</span>\n  <span class=\"hljs-attr\">articles:</span> <span class=\"hljs-string\">\"blog\"</span>         <span class=\"hljs-comment\"># articles' section (`blog` by default)</span>\n  <span class=\"hljs-attr\">twitter:</span> <span class=\"hljs-literal\">true</span>            <span class=\"hljs-comment\"># includes Twitter/X Card meta tags (`true` by default)</span>\n  <span class=\"hljs-attr\">mastodon:</span> <span class=\"hljs-literal\">true</span>           <span class=\"hljs-comment\"># includes Mastodon meta tags (`true` by default)</span>\n  <span class=\"hljs-attr\">dc:</span> <span class=\"hljs-literal\">false</span>                <span class=\"hljs-comment\"># includes Dublin Core meta tags (`false` by default)</span>\n  <span class=\"hljs-attr\">data:</span> <span class=\"hljs-literal\">false</span>              <span class=\"hljs-comment\"># includes JSON-LD structured data (`false` by default)</span></code></pre>\n<h2 id=\"debug\">debug</h2>\n<p>Enables the <em>debug mode</em>, used to display debug information like very verbose logs, Twig dump, Twig profiler, SCSS sourcemap, etc.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">debug:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<p>There are two other ways to enable <em>debug mode</em>:</p>\n<ol>\n<li>Run a command with the <code translate=\"no\">-vvv</code> option</li>\n<li>Set the <code translate=\"no\">CECIL_DEBUG</code> environment variable to <code translate=\"no\">true</code></li>\n</ol>\n<hr>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/content/pages/",
      "url": "https://cecil-945.pages.dev/documentation/content/pages/",
      "title": "Pages and sections",
      "summary": "Anatomy of a page, file prefix, sections, sub-sections and home page.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-06T00:00:00+00:00","content_text": "Pages and sections\nA page is a file made up of a front matter and a body.\nFront matter\nThe front matter is a collection of variables (in key\/value format) surrounded by ---.\nExample:\n---\ntitle: \"The title\"\ndate: 2019-02-21\ntags: [tag 1, tag 2]\ncustomvar: \"Value of customvar\"\n---\nYou can also use &lt;!-- --&gt; or +++ as separator.\nBody\nBody is the main content of a page, it could be written in Markdown or in plain text.\nExample:\n# Header\n\n[toc]\n\n## Sub-Header 1\n\nLorem ipsum dolor [sit amet](https:\/\/example.com), consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n&lt;!-- excerpt --&gt;\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n\n## Sub-Header 2\n\n![Description](\/image.jpg \"Title\")\n\n## Sub-Header 3\n\n:::tip\nThis is advice.\n:::\nFile prefix\nThe filename can contain a prefix to define date or weight variables of the page (used by sortby).\nDefault prefix separators: _ and -.\nYou can customize them with the pages.prefix.separator option.\ndate\nThe date prefix is used to set the date of the page, and must be a valid date format (i.e.: « YYYY-MM-DD »).\nExample:\nIn « 2019-04-23_My blog post.md »:\n\nthe prefix is « 2019-04-23 »\nthe date of the page is « 2019-04-23 »\nthe title of the page is « My blog post »\n\nweight\nThe weight prefix is used to set the sort order of the page, and must be a valid integer value.\nExample:\nIn « 1_The first project.md »:\n\nthe prefix is « 1 »\nthe weight of the page is « 1 »\nthe title of the page is « The first project »\n\nSection\nSome dedicated variables can be used in a custom Section (i.e.: &lt;section&gt;\/index.md).\nsortby\nThe order of pages in a Section can be changed.\nAvailable values are:\n\ndate: more recent first\ntitle: alphabetic order\nweight: lightest first\n\nExample:\n---\nsortby: title\n---\nMore options:\n---\nsortby:\n  variable: date    # \"date\", \"updated\", \"title\" or \"weight\"\n  desc_title: false # used with \"date\" or \"updated\" variable value to sort by desc title order if items have the same date\n  reverse: false    # reversed if true\n---\npagination\nThe global pagination configuration is used by default, but you can change it for a specific Section.\nExample:\n---\npagination:\n  max: 5\n  path: \"page\"\n---\nPagination can be disabled for a Section:\n---\npagination: false\n---\ncascade\nAny variables in cascade are added to the front matter of all sub pages.\nExample:\n---\ncascade:\n  banner: image.jpg\n---\nExisting variables are not overridden.\ncircular\nSet circular to true to enable circular navigation with page.&lt;prev\/next&gt;.\nWith sub-sections, only the circular value of the top level Section is used.\nExample:\n---\ncircular: true\n---\nSub-section\nA nested folder that explicitly contains an index.md file is turned into a sub-section of its parent Section.\n&lt;mywebsite&gt;\n└─ pages\n   └─ blog                 &lt;- Section\n      ├─ index.md\n      ├─ post-1.md         &lt;- Page in Section \"blog\"\n      └─ 2024              &lt;- Sub-section (contains an \"index.md\")\n         ├─ index.md\n         └─ post-2.md      &lt;- Page in Section \"blog\" *and* sub-section \"blog\/2024\"\nA sub-section:\n\nis a Section (same type, variables and layout resolution) available at its own URL (e.g.: \/blog\/2024\/)\nis rendered with the layouts of its parent Sections if it doesn't have its own (e.g.: blog\/list.html.twig)\ncan be nested at any depth (e.g.: blog\/2024\/06\/)\nlists its own pages, and its pages also belong to each of their parent Sections\nis not listed in its parent Section\nis placed in the page.&lt;prev\/next&gt; navigation of its parent Section (according to its sortby), followed by its own pages\n\nA nested folder without an index.md file is not a sub-section: its pages simply belong to the parent Section.\nHome page\nLike another section, Home page support sortby and pagination configuration.\npagesfrom\nSet a valid Section name in pagesfrom to use pages collection from this Section in Home page.\nExample:\n---\npagesfrom: blog\n---",
      "content_html": "<h1>Pages and sections</h1>\n<p>A page is a file made up of a <a href=\"#front-matter\"><strong>front matter</strong></a> and a <a href=\"#body\"><strong>body</strong></a>.</p>\n<h2 id=\"front-matter\">Front matter</h2>\n<p>The <em>front matter</em> is a collection of <a href=\"2-front-matter.md\">variables</a> (in <em>key/value</em> format) surrounded by <code translate=\"no\">---</code>.</p>\n<p><em>Example:</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\">\"The title\"</span>\n<span class=\"hljs-attr\">date:</span> <span class=\"hljs-number\">2019</span><span class=\"hljs-number\">-02</span><span class=\"hljs-number\">-21</span>\n<span class=\"hljs-attr\">tags:</span> <span class=\"hljs-string\">[tag</span> <span class=\"hljs-number\">1</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">tag</span> <span class=\"hljs-number\">2</span><span class=\"hljs-string\">]</span>\n<span class=\"hljs-attr\">customvar:</span> <span class=\"hljs-string\">\"Value of customvar\"</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<aside class=\"note note-info\"><p>You can also use <code translate=\"no\">&lt;!-- --&gt;</code> or <code translate=\"no\">+++</code> as separator.</p></aside>\n<h2 id=\"body\">Body</h2>\n<p><em>Body</em> is the main content of a page, it could be written in <a href=\"3-markdown.md\">Markdown</a> or in plain text.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\"><span class=\"hljs-section\"># Header</span>\n\n[toc]\n\n<span class=\"hljs-section\">## Sub-Header 1</span>\n\nLorem ipsum dolor [<span class=\"hljs-string\">sit amet</span>](<span class=\"hljs-link\">https://example.com</span>), consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n<span class=\"xml\"><span class=\"hljs-comment\">&lt;!-- excerpt --&gt;</span></span>\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n\n<span class=\"hljs-section\">## Sub-Header 2</span>\n\n![<span class=\"hljs-string\">Description</span>](<span class=\"hljs-link\">/image.jpg \"Title\"</span>)\n\n<span class=\"hljs-section\">## Sub-Header 3</span>\n\n:::tip\nThis is advice.\n:::</code></pre>\n<h2 id=\"file-prefix\">File prefix</h2>\n<p>The filename can contain a prefix to define <code translate=\"no\">date</code> or <code translate=\"no\">weight</code> variables of the page (used by <a href=\"../templates/reference/2-sorts.md#sort-by-date\"><code translate=\"no\">sortby</code></a>).</p>\n<aside class=\"note note-info\"><p>Default prefix separators: <code translate=\"no\">_</code> and <code translate=\"no\">-</code>.</p>\n<p>You can customize them with the <a href=\"../configuration/4-pages.md#pages-prefix-separator\"><code translate=\"no\">pages.prefix.separator</code></a> option.</p></aside>\n<h3 id=\"date\">date</h3>\n<p>The <em>date prefix</em> is used to set the <code translate=\"no\">date</code> of the page, and must be a valid date format (i.e.: « YYYY-MM-DD »).</p>\n<p><em>Example:</em></p>\n<p>In « 2019-04-23_My blog post.md »:</p>\n<ul>\n<li>the prefix is « 2019-04-23 »</li>\n<li>the <code translate=\"no\">date</code> of the page is « 2019-04-23 »</li>\n<li>the <code translate=\"no\">title</code> of the page is « My blog post »</li>\n</ul>\n<h3 id=\"weight\">weight</h3>\n<p>The <em>weight prefix</em> is used to set the sort order of the page, and must be a valid integer value.</p>\n<p><em>Example:</em></p>\n<p>In « 1_The first project.md »:</p>\n<ul>\n<li>the prefix is « 1 »</li>\n<li>the <code translate=\"no\">weight</code> of the page is « 1 »</li>\n<li>the <code translate=\"no\">title</code> of the page is « The first project »</li>\n</ul>\n<h2 id=\"section\">Section</h2>\n<p>Some dedicated variables can be used in a custom <em>Section</em> (i.e.: <code translate=\"no\">&lt;section&gt;/index.md</code>).</p>\n<h3 id=\"sortby\">sortby</h3>\n<p>The order of pages in a <em>Section</em> can be changed.</p>\n<p>Available values are:</p>\n<ul>\n<li><code translate=\"no\">date</code>: more recent first</li>\n<li><code translate=\"no\">title</code>: alphabetic order</li>\n<li><code translate=\"no\">weight</code>: lightest first</li>\n</ul>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">sortby:</span> <span class=\"hljs-string\">title</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<p><strong>More options:</strong></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">sortby:</span>\n  <span class=\"hljs-attr\">variable:</span> <span class=\"hljs-string\">date</span>    <span class=\"hljs-comment\"># \"date\", \"updated\", \"title\" or \"weight\"</span>\n  <span class=\"hljs-attr\">desc_title:</span> <span class=\"hljs-literal\">false</span> <span class=\"hljs-comment\"># used with \"date\" or \"updated\" variable value to sort by desc title order if items have the same date</span>\n  <span class=\"hljs-attr\">reverse:</span> <span class=\"hljs-literal\">false</span>    <span class=\"hljs-comment\"># reversed if true</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<h3 id=\"pagination\">pagination</h3>\n<p>The global <a href=\"../configuration/4-pages.md#pages-pagination\">pagination configuration</a> is used by default, but you can change it for a specific <em>Section</em>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">pagination:</span>\n  <span class=\"hljs-attr\">max:</span> <span class=\"hljs-number\">5</span>\n  <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">\"page\"</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<p>Pagination can be disabled for a <em>Section</em>:</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<h3 id=\"cascade\">cascade</h3>\n<p>Any variables in <code translate=\"no\">cascade</code> are added to the front matter of all <em>sub pages</em>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">cascade:</span>\n  <span class=\"hljs-attr\">banner:</span> <span class=\"hljs-string\">image.jpg</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<aside class=\"note note-info\"><p>Existing variables are not overridden.</p></aside>\n<h3 id=\"circular\">circular</h3>\n<p>Set <code translate=\"no\">circular</code> to <code translate=\"no\">true</code> to enable circular navigation with <a href=\"../templates/2-variables.md#page-prev-next\"><em>page.&lt;prev/next&gt;</em></a>.</p>\n<aside class=\"note note-info\"><p>With <a href=\"#sub-section\">sub-sections</a>, only the <code translate=\"no\">circular</code> value of the top level <em>Section</em> is used.</p></aside>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">circular:</span> <span class=\"hljs-literal\">true</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<h3 id=\"sub-section\">Sub-section</h3>\n<p>A nested folder that explicitly contains an <code translate=\"no\">index.md</code> file is turned into a <em>sub-section</em> of its parent <em>Section</em>.</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">&lt;mywebsite&gt;\n└─ pages\n   └─ blog                 &lt;- Section\n      ├─ index.md\n      ├─ post-1.md         &lt;- Page in Section \"blog\"\n      └─ 2024              &lt;- Sub-section (contains an \"index.md\")\n         ├─ index.md\n         └─ post-2.md      &lt;- Page in Section \"blog\" *and* sub-section \"blog/2024\"</code></pre>\n<p>A <em>sub-section</em>:</p>\n<ul>\n<li>is a <em>Section</em> (same type, variables and <a href=\"../templates/1-lookup-rules.md#type-section\">layout</a> resolution) available at its own URL (e.g.: <code translate=\"no\">/blog/2024/</code>)</li>\n<li>is rendered with the layouts of its parent <em>Sections</em> if it doesn't have its own (e.g.: <code translate=\"no\">blog/list.html.twig</code>)</li>\n<li>can be nested at any depth (e.g.: <code translate=\"no\">blog/2024/06/</code>)</li>\n<li>lists its own pages, and its pages also belong to each of their parent <em>Sections</em></li>\n<li>is <strong>not</strong> listed in its parent <em>Section</em></li>\n<li>is placed in the <a href=\"../templates/2-variables.md#page-prev-next\"><em>page.&lt;prev/next&gt;</em></a> navigation of its parent <em>Section</em> (according to its <code translate=\"no\">sortby</code>), followed by its own pages</li>\n</ul>\n<aside class=\"note note-info\"><p>A nested folder <strong>without</strong> an <code translate=\"no\">index.md</code> file is not a <em>sub-section</em>: its pages simply belong to the parent <em>Section</em>.</p></aside>\n<h2 id=\"home-page\">Home page</h2>\n<p>Like another section, <em>Home page</em> support <code translate=\"no\">sortby</code> and <code translate=\"no\">pagination</code> configuration.</p>\n<h3 id=\"pagesfrom\">pagesfrom</h3>\n<p>Set a valid <em>Section</em> name in <code translate=\"no\">pagesfrom</code> to use pages collection from this <em>Section</em> in <em>Home page</em>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">pagesfrom:</span> <span class=\"hljs-string\">blog</span>\n<span class=\"hljs-meta\">---</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/templates/lookup-rules/",
      "url": "https://cecil-945.pages.dev/documentation/templates/lookup-rules/",
      "title": "Organization and lookup rules",
      "summary": "Kinds of templates, naming convention, built-in templates and how a template is chosen for a page.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Organization and lookup rules\nCecil renders pages with Twig templates: this page explains how templates are organized and how the right one is chosen for each page.\nFiles organization\nKinds of templates\nThere are three kinds of templates: layouts, components, and other templates. Layouts are used to render pages, and each layout can include templates and components.\nNaming convention\nTemplate files are stored in the layouts\/ directory and must be named according to the following convention:\nlayouts\/(&lt;section&gt;\/)&lt;type&gt;|&lt;layout&gt;.&lt;format&gt;(.&lt;language&gt;).twig\n\n&lt;section&gt; (optional)\nThe section of the page (e.g.: blog).\n&lt;type&gt;\nThe page type: home (or index) for homepage, list for list, page for page, etc. (See Lookup rules for details).\n&lt;layout&gt; (optional)\nThe custom layout name defined in the front matter of the page (e.g.: layout: my-layout).\n&lt;format&gt;\nThe output format of the rendered page (e.g.: html, rss, json, xml, etc.).\n&lt;language&gt; (optional)\nThe language of the page (e.g.: fr).\n\nExamples:\nlayouts\/home.html.twig       # `type` is \"homepage\"\nlayouts\/page.html.twig       # `type` is \"page\"\nlayouts\/page.html.fr.twig    # `type` is \"page\" and `language` is \"fr\"\nlayouts\/my-layout.html.twig  # `layout` is \"my-layout\"\nlayouts\/blog\/list.html.twig  # `section` is \"blog\"\nlayouts\/blog\/list.rss.twig   # `section` is \"blog\" and `format` is \"rss\"\n&lt;my-site&gt;\n├─ ...\n├─ layouts\n|  ├─ index.html.twig      # Used by type \"homepage\"\n|  ├─ list.html.twig       # Used by types \"homepage\" and \"section\"\n|  ├─ list.rss.twig        # Used by types \"homepage\" and \"section\", for RSS output format\n|  ├─ page.html.twig       # Used by type \"page\"\n|  ├─ taxonomy\n|  |  ├─ tags.html.twig    # Used by type \"vocabulary\" of `tags` (list of terms)\n|  |  └─ tag.html.twig     # Used by type \"term\" of `tags` (list of pages)\n|  ├─ my-layout.html.twig  # Used by pages with `layout: my-layout` in the front matter\n|  ├─ ...\n|  └─ partials             # Included templates\n|     ├─ footer.html.twig\n|     └─ ...\n└─ themes                  # Themes layouts and templates\n   └─ ...\nBuilt-in templates\nCecil comes with a set of built-in templates.\nIf you need to modify built-in templates, you can easily extract them via the following command: they will be copied in the layouts directory of your site.\nphp cecil.phar util:templates:extract\nLookup rules\nIn most of cases you don’t need to specify the layout: Cecil selects the most appropriate layout, according to the page type.\nFor example, the HTML output of home page (index.md) will be rendered:\n\nwith my-layout.html.twig if the layout variable is set to \"my-layout\" (in the front matter)\nif not, with index.html.twig if the file exists\nif not, with home.html.twig if the file exists\nif not, with list.html.twig if the file exists\n\nAll rules are detailed below, for each page type, in the priority order.\nType homepage\n\n&lt;layout&gt;.&lt;format&gt;.twig\nindex.&lt;format&gt;.twig\nhome.&lt;format&gt;.twig\nlist.&lt;format&gt;.twig\n_default\/&lt;layout&gt;.&lt;format&gt;.twig\n_default\/index.&lt;format&gt;.twig\n_default\/home.&lt;format&gt;.twig\n_default\/list.&lt;format&gt;.twig\n_default\/page.&lt;format&gt;.twig\n\nType page\n\n&lt;section&gt;\/&lt;layout&gt;.&lt;format&gt;.twig\n&lt;layout&gt;.&lt;format&gt;.twig\n&lt;section&gt;\/page.&lt;format&gt;.twig\n_default\/&lt;layout&gt;.&lt;format&gt;.twig\npage.&lt;format&gt;.twig\n_default\/page.&lt;format&gt;.twig\n\nType section\n\n&lt;layout&gt;.&lt;format&gt;.twig\n&lt;section&gt;\/index.&lt;format&gt;.twig\n&lt;section&gt;\/list.&lt;format&gt;.twig\nsection\/&lt;section&gt;.&lt;format&gt;.twig\n&lt;parent&gt;\/index.&lt;format&gt;.twig, &lt;parent&gt;\/list.&lt;format&gt;.twig and section\/&lt;parent&gt;.&lt;format&gt;.twig, for each parent section of a sub-section (nearest first)\n_default\/section.&lt;format&gt;.twig\nlist.&lt;format&gt;.twig\n_default\/list.&lt;format&gt;.twig\n\nThe &lt;section&gt; of a sub-section is its full path (e.g.: blog\/2024), and a sub-section falls back to the templates of its parent sections: if blog\/2024\/list.html.twig doesn’t exist, the sub-section blog\/2024 is rendered with blog\/list.html.twig.\nType vocabulary\n\ntaxonomy\/&lt;plural&gt;.&lt;format&gt;.twig\nvocabulary.&lt;format&gt;.twig\n_default\/vocabulary.&lt;format&gt;.twig\n\nType term\n\ntaxonomy\/&lt;plural&gt;\/&lt;term&gt;.&lt;format&gt;.twig\ntaxonomy\/&lt;singular&gt;.&lt;format&gt;.twig\nterm.&lt;format&gt;.twig\n_default\/term.&lt;format&gt;.twig\n_default\/list.&lt;format&gt;.twig\n\nThe vocabulary template is named after the plural (e.g.: taxonomy\/categories.html.twig for \/categories\/), whereas the term template is named after the singular (e.g.: taxonomy\/category.html.twig for \/categories\/data-sovereignty\/).\n&lt;term&gt; is the slugified term name: a dedicated template for the term \"Data Sovereignty\" of the categories vocabulary is taxonomy\/categories\/data-sovereignty.html.twig.\nMost of those layouts are available by default, see built-in templates.",
      "content_html": "<h1>Organization and lookup rules</h1>\n<p>Cecil renders pages with Twig templates: this page explains how templates are organized and how the right one is chosen for each page.</p>\n<h2 id=\"files-organization\">Files organization</h2>\n<h3 id=\"kinds-of-templates\">Kinds of templates</h3>\n<p>There are three kinds of templates: <strong><em>layouts</em></strong>, <strong><em>components</em></strong>, and <strong><em>other templates</em></strong>. <em>Layouts</em> are used to render <a href=\"../content/1-pages.md\">pages</a>, and each layout can <a href=\"https://twig.symfony.com/doc/templates.html#including-other-templates\" target=\"_blank\" rel=\"noopener noreferrer\">include templates</a> and <a href=\"4-components.md\">components</a>.</p>\n<h3 id=\"naming-convention\">Naming convention</h3>\n<p>Template files are stored in the <code translate=\"no\">layouts/</code> directory and must be named according to the following convention:</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">layouts/(&lt;section&gt;/)&lt;type&gt;|&lt;layout&gt;.&lt;format&gt;(.&lt;language&gt;).twig</code></pre>\n<dl>\n<dt><code translate=\"no\">&lt;section&gt;</code> (<em>optional</em>)</dt>\n<dd>The section of the page (e.g.: <code translate=\"no\">blog</code>).</dd>\n<dt><code translate=\"no\">&lt;type&gt;</code></dt>\n<dd>The page type: <code translate=\"no\">home</code> (or <code translate=\"no\">index</code>) for <em>homepage</em>, <code translate=\"no\">list</code> for <em>list</em>, <code translate=\"no\">page</code> for <em>page</em>, etc. (See <a href=\"#lookup-rules\"><em>Lookup rules</em></a> for details).</dd>\n<dt><code translate=\"no\">&lt;layout&gt;</code> (<em>optional</em>)</dt>\n<dd>The custom layout name defined in the <a href=\"../content/1-pages.md#front-matter\">front matter</a> of the page (e.g.: <code translate=\"no\">layout: my-layout</code>).</dd>\n<dt><code translate=\"no\">&lt;format&gt;</code></dt>\n<dd>The <a href=\"../configuration/8-output.md#output-formats\">output format</a> of the rendered page (e.g.: <code translate=\"no\">html</code>, <code translate=\"no\">rss</code>, <code translate=\"no\">json</code>, <code translate=\"no\">xml</code>, etc.).</dd>\n<dt><code translate=\"no\">&lt;language&gt;</code> (<em>optional</em>)</dt>\n<dd>The language of the page (e.g.: <code translate=\"no\">fr</code>).</dd>\n</dl>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">layouts/home.html.twig       # `type` is \"homepage\"\nlayouts/page.html.twig       # `type` is \"page\"\nlayouts/page.html.fr.twig    # `type` is \"page\" and `language` is \"fr\"\nlayouts/my-layout.html.twig  # `layout` is \"my-layout\"\nlayouts/blog/list.html.twig  # `section` is \"blog\"\nlayouts/blog/list.rss.twig   # `section` is \"blog\" and `format` is \"rss\"</code></pre>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">&lt;my-site&gt;\n├─ ...\n├─ layouts\n|  ├─ index.html.twig      # Used by type \"homepage\"\n|  ├─ list.html.twig       # Used by types \"homepage\" and \"section\"\n|  ├─ list.rss.twig        # Used by types \"homepage\" and \"section\", for RSS output format\n|  ├─ page.html.twig       # Used by type \"page\"\n|  ├─ taxonomy\n|  |  ├─ tags.html.twig    # Used by type \"vocabulary\" of `tags` (list of terms)\n|  |  └─ tag.html.twig     # Used by type \"term\" of `tags` (list of pages)\n|  ├─ my-layout.html.twig  # Used by pages with `layout: my-layout` in the front matter\n|  ├─ ...\n|  └─ partials             # Included templates\n|     ├─ footer.html.twig\n|     └─ ...\n└─ themes                  # Themes layouts and templates\n   └─ ...</code></pre>\n<h3 id=\"built-in-templates\">Built-in templates</h3>\n<p>Cecil comes with a set of <a href=\"https://github.com/Cecilapp/Cecil/tree/main/resources/layouts\" target=\"_blank\" rel=\"noopener noreferrer\">built-in templates</a>.</p>\n<aside class=\"note note-tip\"><p>If you need to modify built-in templates, you can easily extract them via the following command: they will be copied in the <code translate=\"no\">layouts</code> directory of your site.</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar util:templates:extract</code></pre></aside>\n<h2 id=\"lookup-rules\">Lookup rules</h2>\n<p>In most of cases <strong>you don’t need to specify the layout</strong>: Cecil selects the most appropriate layout, according to the <strong>page type</strong>.</p>\n<p>For example, the HTML output of <strong>home page</strong> (<code translate=\"no\">index.md</code>) will be rendered:</p>\n<ol>\n<li>with <code translate=\"no\">my-layout.html.twig</code> if the <code translate=\"no\">layout</code> variable is set to \"my-layout\" (in the front matter)</li>\n<li>if not, with <code translate=\"no\">index.html.twig</code> if the file exists</li>\n<li>if not, with <code translate=\"no\">home.html.twig</code> if the file exists</li>\n<li>if not, with <code translate=\"no\">list.html.twig</code> if the file exists</li>\n</ol>\n<p>All rules are detailed below, for each page type, in the priority order.</p>\n<h3 id=\"type-homepage\">Type <em>homepage</em></h3>\n<ol>\n<li><code translate=\"no\">&lt;layout&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">index.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">home.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">list.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/&lt;layout&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/index.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/home.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/list.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/page.&lt;format&gt;.twig</code></li>\n</ol>\n<h3 id=\"type-page\">Type <em>page</em></h3>\n<ol>\n<li><code translate=\"no\">&lt;section&gt;/&lt;layout&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">&lt;layout&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">&lt;section&gt;/page.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/&lt;layout&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">page.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/page.&lt;format&gt;.twig</code></li>\n</ol>\n<h3 id=\"type-section\">Type <em>section</em></h3>\n<ol>\n<li><code translate=\"no\">&lt;layout&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">&lt;section&gt;/index.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">&lt;section&gt;/list.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">section/&lt;section&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">&lt;parent&gt;/index.&lt;format&gt;.twig</code>, <code translate=\"no\">&lt;parent&gt;/list.&lt;format&gt;.twig</code> and <code translate=\"no\">section/&lt;parent&gt;.&lt;format&gt;.twig</code>, for each parent section of a sub-section (nearest first)</li>\n<li><code translate=\"no\">_default/section.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">list.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/list.&lt;format&gt;.twig</code></li>\n</ol>\n<aside class=\"note note-tip\"><p>The <code translate=\"no\">&lt;section&gt;</code> of a <a href=\"../content/1-pages.md#sub-section\">sub-section</a> is its full path (e.g.: <code translate=\"no\">blog/2024</code>), and a sub-section falls back to the templates of its parent sections: if <code translate=\"no\">blog/2024/list.html.twig</code> doesn’t exist, the sub-section <code translate=\"no\">blog/2024</code> is rendered with <code translate=\"no\">blog/list.html.twig</code>.</p></aside>\n<h3 id=\"type-vocabulary\">Type <em>vocabulary</em></h3>\n<ol>\n<li><code translate=\"no\">taxonomy/&lt;plural&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">vocabulary.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/vocabulary.&lt;format&gt;.twig</code></li>\n</ol>\n<h3 id=\"type-term\">Type <em>term</em></h3>\n<ol>\n<li><code translate=\"no\">taxonomy/&lt;plural&gt;/&lt;term&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">taxonomy/&lt;singular&gt;.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">term.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/term.&lt;format&gt;.twig</code></li>\n<li><code translate=\"no\">_default/list.&lt;format&gt;.twig</code></li>\n</ol>\n<aside class=\"note note-important\"><p>The <strong>vocabulary</strong> template is named after the <strong>plural</strong> (e.g.: <code translate=\"no\">taxonomy/categories.html.twig</code> for <code translate=\"no\">/categories/</code>), whereas the <strong>term</strong> template is named after the <strong>singular</strong> (e.g.: <code translate=\"no\">taxonomy/category.html.twig</code> for <code translate=\"no\">/categories/data-sovereignty/</code>).</p></aside>\n<aside class=\"note note-tip\"><p><code translate=\"no\">&lt;term&gt;</code> is the slugified term name: a dedicated template for the term \"Data Sovereignty\" of the <code translate=\"no\">categories</code> vocabulary is <code translate=\"no\">taxonomy/categories/data-sovereignty.html.twig</code>.</p></aside>\n<aside class=\"note note-info\"><p>Most of those layouts are available by default, see <a href=\"https://github.com/Cecilapp/Cecil/tree/main/resources/layouts\" target=\"_blank\" rel=\"noopener noreferrer\">built-in templates</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/templates/reference/functions/",
      "url": "https://cecil-945.pages.dev/documentation/templates/reference/functions/",
      "title": "Functions",
      "summary": "url, html, readtime, hash, cache_key, getenv, dump, etc.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Functions\n\nFunctions can be called to generate content. Functions are called by their name followed by parentheses (()) and may have arguments.\n\nurl\nCreates a valid URL for a page, a menu entry, an asset, a page ID or a path.\n{{ url(value, {options}) }}\n\n\n\nOption\nDescription\nType\nDefault\n\n\n\n\ncanonical\nPrefix URL with baseurl or use canonical.url if exists.\nboolean\nfalse\n\n\nformat\nDefines page output format (e.g.: json).\nstring\nhtml\n\n\nlanguage\nDefines page language (e.g.: fr).\nstring\nnull\n\n\n\nExamples:\n{# page #}\n{{ url(page) }}\n{{ url(page, {canonical: true}) }}\n{{ url(page, {format: json}) }}\n{{ url(page, {language: fr}) }}\n{# menu entry #}\n{{ url(site.menus.main.about) }}\n{# asset #}\n{{ url(asset('styles.css')) }}\n{# page ID #}\n{{ url('page-id') }}\n{# path #}\n{{ url('about-me\/') }}\n{{ url('tags\/' ~ tag) }}\nFor convenience the url function is also available as a filter:\n{# page #}\n{{ page|url }}\n{{ page|url({canonical: true, format: json, language: fr}) }}\n{# asset #}\n{{ asset('styles.css')|url }}\nWhen the value is a string, url() slugifies it to find a matching page ID (e.g.: url('tags\/My Tag') returns the URL of the page tags\/my-tag). If no page matches, the string is kept as a path, with invalid characters (e.g.: spaces) percent-encoded.\nhtml\nCreates an HTML element from an asset (or an array of assets with custom attributes).\n{{ html(asset, {attributes}, {options}) }}\n{# dedicated functions for each common type of asset #}\n{{ css(asset) }}\n{{ js(asset) }}\n{{ image(asset) }}\n{{ audio(asset) }}\n{{ video(asset) }}\n\n\n\nOption\nDescription\nType\n\n\n\n\nattributes\nAdds name=\"value\" couple to the HTML element.\narray\n\n\noptions\n{preload: boolean}: preloads.For images:{formats: array}: adds alternative formats.{responsive: bool|string}: adds responsive images (based on width or pixels density).{placeholder: string}: fills the image background before loading (color or lqip).\narray\n\n\n\nSince version 8.42.0, the html function replace the deprecated html filter.\nYou can define a global default behavior of images options (formats, responsive and placeholder) through the layouts configuration.\nWhen layouts.images.dark_suffix is configured (e.g. .dark), Cecil automatically looks for a dark variant of each image (e.g. photo.dark.jpg alongside photo.jpg) and generates a &lt;picture&gt; element with a &lt;source media=\"(prefers-color-scheme: dark)\"&gt;.\nIn the same way, when layouts.images.mobile_suffix is configured (e.g. .mobile), Cecil looks for a mobile variant of each image (e.g. photo.mobile.jpg) and adds a &lt;source&gt; with the layouts.images.mobile_media_query media query. If a dark variant of the mobile image exists (e.g. photo.mobile.dark.jpg), it is used on mobile with dark color scheme.\nExamples:\n{# CSS with an attribute #}\n{{ html(asset('print.css'), {media: 'print'}) }}\n{# CSS with an attribute and an option #}\n{{ html(asset('styles.css'), {title: 'Main theme'}, {preload: true}) }}\n{# Array of assets with media query #}\n{{ html([\n  {asset: asset('css\/style.css')},\n  {asset: asset('css\/style-dark.css'), attributes: {media: '(prefers-color-scheme: dark)'}}\n]) }}\n{# JavaScript #}\n{{ html(asset('script.js')) }}\n{# image without specific attributes nor options #}\n{{ html(asset('image.png')) }}\n{# image with specific attributes, responsive images and alternative formats #}\n{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {responsive: true, formats: ['avif', 'webp']}) }}\n{# image with responsive pixels density images #}\n{{ html(asset('image.jpg'), options={responsive: 'density'}, attributes={width: 256}) }}\n{# image with a Low-Quality Image Placeholder #}\n{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {placeholder: 'lqip'}) }}\n{# Audio #}\n{{ html(asset('audio.mp3')) }}\n{# Video #}\n{{ html(asset('video.mp4')) }}\nFor convenience the html function stay available as a filter (but is considered as deprecated):\n{{ asset|html({attributes}, {options}) }}\nreadtime\nDetermines read time of a text, in minutes.\n{{ readtime(value) }}\nExample:\n{{ readtime(page.content) }} min\nhash\nCalculates the hash of an object, an array or a string with a given algorithm.\n{{ hash(value, algorithm) }}\nalgorithm can be any algorithm supported by PHP's hash() function (e.g.: md5, sha256, etc.). Default is xxh128.\nExample:\n{{ hash('my string', 'sha256') }}\ncache_key\nCalculates a cache key for fragments cache based on a name and an optional value.\n{% cache cache_key(name, value) %}\n  {# cacheable content #}\n{% endcache %}\nThe function adds a hash of the value (could be a string, an array or an object) to the name (and the current language and build ID to be sure the generated cache key is unique) so if the value is changed the cache key is changed too and the cache is automatically cleared.\ngetenv\nGets the value of an environment variable from its key.\n{{ getenv(var) }}\nExample:\n{{ getenv('VAR') }}\ndump\nThe dump function dumps information about a template variable. This is mostly useful to debug a template that does not behave as expected by introspecting its variables:\n{{ dump(user) }}\nThe debug mode must be enabled.\nd\nThe d() function is the HTML version of dump() and use the Symfony VarDumper Component behind the scenes.\n{{ d(variable, {theme: light}) }}\n\nIf variable is not provided then the function returns the current Twig context\nAvailable themes are « light » (default) and « dark »\n\nThe debug mode must be enabled.",
      "content_html": "<h1>Functions</h1>\n<blockquote>\n<p><a href=\"https://twig.symfony.com/doc/functions/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">Functions</a> can be called to generate content. Functions are called by their name followed by parentheses (<code translate=\"no\">()</code>) and may have arguments.</p>\n</blockquote>\n<h2 id=\"url\">url</h2>\n<p>Creates a valid URL for a page, a menu entry, an asset, a page ID or a path.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ url(value, {options}) }}</span></code></pre>\n<table>\n<thead>\n<tr>\n<th>Option</th>\n<th>Description</th>\n<th>Type</th>\n<th>Default</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>canonical</td>\n<td>Prefix URL with <a href=\"../../configuration/1-site.md#baseurl\"><code translate=\"no\">baseurl</code></a> or use <a href=\"../../configuration/1-site.md#metatags-options\"><code translate=\"no\">canonical.url</code></a> if exists.</td>\n<td>boolean</td>\n<td><code translate=\"no\">false</code></td>\n</tr>\n<tr>\n<td>format</td>\n<td>Defines page <a href=\"../../configuration/8-output.md#output-formats\">output format</a> (e.g.: <code translate=\"no\">json</code>).</td>\n<td>string</td>\n<td><code translate=\"no\">html</code></td>\n</tr>\n<tr>\n<td>language</td>\n<td>Defines page <a href=\"../../configuration/2-languages.md#language\">language</a> (e.g.: <code translate=\"no\">fr</code>).</td>\n<td>string</td>\n<td>null</td>\n</tr>\n</tbody>\n</table>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# page #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(page) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(page, {canonical: true}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(page, {format: json}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(page, {language: fr}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# menu entry #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(site.menus.main.about) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# asset #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(asset('styles.css')) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# page ID #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url('page-id') }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# path #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url('about-me/') }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url('tags/' ~ tag) }}</span></code></pre>\n<aside class=\"note note-info\"><p>For convenience the <code translate=\"no\">url</code> function is also available as a filter:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# page #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page|url }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page|url({canonical: true, format: json, language: fr}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# asset #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ asset('styles.css')|url }}</span></code></pre></aside>\n<aside class=\"note note-tip\"><p>When the value is a string, <code translate=\"no\">url()</code> slugifies it to find a matching page ID (e.g.: <code translate=\"no\">url('tags/My Tag')</code> returns the URL of the page <code translate=\"no\">tags/my-tag</code>). If no page matches, the string is kept as a path, with invalid characters (e.g.: spaces) percent-encoded.</p></aside>\n<h2 id=\"html\">html</h2>\n<p>Creates an HTML element from an asset (or an array of assets with custom attributes).</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ html(asset, {attributes}, {options}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# dedicated functions for each common type of asset #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ css(asset) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ js(asset) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ image(asset) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ audio(asset) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ video(asset) }}</span></code></pre>\n<table>\n<thead>\n<tr>\n<th>Option</th>\n<th>Description</th>\n<th>Type</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>attributes</td>\n<td>Adds <code translate=\"no\">name=\"value\"</code> couple to the HTML element.</td>\n<td>array</td>\n</tr>\n<tr>\n<td>options</td>\n<td><code translate=\"no\">{preload: boolean}</code>: preloads.<br>For images:<br><code translate=\"no\">{formats: array}</code>: adds alternative formats.<br><code translate=\"no\">{responsive: bool|string}</code>: adds responsive images (based on <code translate=\"no\">width</code> or pixels <code translate=\"no\">density</code>).<br><code translate=\"no\">{placeholder: string}</code>: fills the image background before loading (<code translate=\"no\">color</code> or <code translate=\"no\">lqip</code>).</td>\n<td>array</td>\n</tr>\n</tbody>\n</table>\n<aside class=\"note note-warning\"><p>Since version <ins>8.42.0</ins>, the <code translate=\"no\">html</code> function replace the deprecated <code translate=\"no\">html</code> filter.</p></aside>\n<aside class=\"note note-tip\"><p>You can define a global default behavior of images options (<code translate=\"no\">formats</code>, <code translate=\"no\">responsive</code> and <code translate=\"no\">placeholder</code>) through the <a href=\"../../configuration/7-layouts.md#layouts-images\">layouts configuration</a>.</p>\n<p>When <a href=\"../../configuration/7-layouts.md#layouts-images\"><code translate=\"no\">layouts.images.dark_suffix</code></a> is configured (e.g. <code translate=\"no\">.dark</code>), Cecil automatically looks for a dark variant of each image (e.g. <code translate=\"no\">photo.dark.jpg</code> alongside <code translate=\"no\">photo.jpg</code>) and generates a <code translate=\"no\">&lt;picture&gt;</code> element with a <code translate=\"no\">&lt;source media=\"(prefers-color-scheme: dark)\"&gt;</code>.</p>\n<p>In the same way, when <a href=\"../../configuration/7-layouts.md#layouts-images\"><code translate=\"no\">layouts.images.mobile_suffix</code></a> is configured (e.g. <code translate=\"no\">.mobile</code>), Cecil looks for a mobile variant of each image (e.g. <code translate=\"no\">photo.mobile.jpg</code>) and adds a <code translate=\"no\">&lt;source&gt;</code> with the <a href=\"../../configuration/7-layouts.md#layouts-images\"><code translate=\"no\">layouts.images.mobile_media_query</code></a> media query. If a dark variant of the mobile image exists (e.g. <code translate=\"no\">photo.mobile.dark.jpg</code>), it is used on mobile with dark color scheme.</p></aside>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# CSS with an attribute #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('print.css'), {media: 'print'}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# CSS with an attribute and an option #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('styles.css'), {title: 'Main theme'}, {preload: true}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# Array of assets with media query #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html([\n  {asset: asset('css/style.css')},\n  {asset: asset('css/style-dark.css'), attributes: {media: '(prefers-color-scheme: dark)'}}</span><span class=\"xml\">\n]) }}\n</span><span class=\"hljs-comment\">{# JavaScript #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('script.js')) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# image without specific attributes nor options #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('image.png')) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# image with specific attributes, responsive images and alternative formats #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {responsive: true, formats: ['avif', 'webp']}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# image with responsive pixels density images #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('image.jpg'), options={responsive: 'density'}, attributes={width: 256}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# image with a Low-Quality Image Placeholder #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {placeholder: 'lqip'}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# Audio #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('audio.mp3')) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# Video #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('video.mp4')) }}</span></code></pre>\n<aside class=\"note note-info\"><p>For convenience the <code translate=\"no\">html</code> function stay available as a filter (but is considered as deprecated):</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset|html({attributes}, {options}) }}</span></code></pre></aside>\n<h2 id=\"readtime\">readtime</h2>\n<p>Determines read time of a text, in minutes.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ readtime(value) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ readtime(page.content) }}</span><span class=\"xml\"> min</span></code></pre>\n<h2 id=\"hash\">hash</h2>\n<p>Calculates the hash of an object, an array or a string with a given algorithm.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ hash(value, algorithm) }}</span></code></pre>\n<p><code translate=\"no\">algorithm</code> can be any algorithm supported by PHP's <code translate=\"no\">hash()</code> function (e.g.: <code translate=\"no\">md5</code>, <code translate=\"no\">sha256</code>, etc.). Default is <code translate=\"no\">xxh128</code>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ hash('my string', 'sha256') }}</span></code></pre>\n<h2 id=\"cache-key\">cache_key</h2>\n<p>Calculates a cache key for <a href=\"../6-cache.md#fragments-cache\"><em>fragments</em> cache</a> based on a name and an optional value.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">cache</span> cache_key(name, value) %}</span><span class=\"xml\">\n  </span><span class=\"hljs-comment\">{# cacheable content #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endcache</span> %}</span></code></pre>\n<p>The function adds a hash of the value (could be a string, an array or an object) to the name (and the current language and build ID to be sure the generated cache key is unique) so if the value is changed the cache key is changed too and the cache is automatically cleared.</p>\n<h2 id=\"getenv\">getenv</h2>\n<p>Gets the value of an environment variable from its key.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ getenv(var) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ getenv('VAR') }}</span></code></pre>\n<h2 id=\"dump\">dump</h2>\n<p>The <code translate=\"no\">dump</code> function dumps information about a template variable. This is mostly useful to debug a template that does not behave as expected by introspecting its variables:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ <span class=\"hljs-name\">dump</span><span class=\"hljs-params\">(user)</span> }}</span></code></pre>\n<aside class=\"note note-important\"><p>The <a href=\"../../configuration/1-site.md#debug\"><em>debug mode</em></a> must be enabled.</p></aside>\n<h2 id=\"d\">d</h2>\n<p>The <code translate=\"no\">d()</code> function is the HTML version of <a href=\"#dump\"><code translate=\"no\">dump()</code></a> and use the <a href=\"https://symfony.com/doc/5.4/components/var_dumper.html\" target=\"_blank\" rel=\"noopener noreferrer\">Symfony VarDumper Component</a> behind the scenes.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ d(variable, {theme: light}) }}</span></code></pre>\n<ul>\n<li>If <em>variable</em> is not provided then the function returns the current Twig context</li>\n<li>Available themes are « light » (default) and « dark »</li>\n</ul>\n<aside class=\"note note-important\"><p>The <a href=\"../../configuration/1-site.md#debug\"><em>debug mode</em></a> must be enabled.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/assets/processing/",
      "url": "https://cecil-945.pages.dev/documentation/assets/processing/",
      "title": "CSS, JavaScript and processing",
      "summary": "Compile Sass, minify, fingerprint, inline or embed assets.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "CSS, JavaScript and processing\nCecil provides Twig filters to compile Sass, minify, fingerprint, inline or embed CSS and JavaScript assets.\nfingerprint\nAdd the file content finger print to the file name.\n{{ asset(path)|fingerprint }}\n{{ path|fingerprint }}\nExamples:\n{{ asset('styles.css')|fingerprint }}\nminify\nMinifying a CSS or a JavaScript file.\n{{ asset(path)|minify }}\nExamples:\n{{ asset('styles.css')|minify }}\n{{ asset('scripts.js')|minify }}\nminify_css\nMinifying a CSS string.\n{{ variable|minify_css }}\n{% apply minify_css %}\n{# CSS here #}\n{% endapply %}\nExamples:\n{% set styles = 'some CSS here' %}\n{{ styles|minify_css }}\n&lt;style&gt;\n{% apply minify_css %}\n  html {\n    background-color: #fcfcfc;\n    color: #444;\n  }\n{% endapply %}\n&lt;\/style&gt;\nminify_js\nMinifying a JavaScript string.\n{{ variable|minify_js }}\n{% apply minify_js %}\n{# JavaScript here #}\n{% endapply %}\nExamples:\n{% set script = 'some JavaScript here' %}\n{{ script|minify_js }}\n&lt;script&gt;\n{% apply minify_js %}\n  var test = 'test';\n  console.log(test);\n{% endapply %}\n&lt;\/script&gt;\nscss_to_css\nCompiles a Sass string to CSS.\n{{ variable|scss_to_css }}\n{% apply scss_to_css %}\n{# SCSS here #}\n{% endapply %}\nAlias: sass_to_css.\nExamples:\n{% set scss = 'some SCSS here' %}\n{{ scss|scss_to_css }}\n&lt;style&gt;\n{% apply scss_to_css %}\n  $color: #fcfcfc;\n  div {\n    color: lighten($color, 20%);\n  }\n{% endapply %}\n&lt;\/style&gt;\nto_css\nCompiles a Sass file to CSS.\n{{ asset(path)|to_css }}\n{{ path|to_css }}\nExamples:\n{{ asset('styles.scss')|to_css }}\ninline\nOutputs the content of an Asset.\n{{ asset(path)|inline }}\nExample:\n{{ asset('styles.css')|inline }}\ndataurl\nReturns the data URL of an asset.\n{{ asset(path)|dataurl }}\n{{ asset(image_path)|dataurl }}",
      "content_html": "<h1>CSS, JavaScript and processing</h1>\n<p>Cecil provides Twig filters to compile Sass, minify, fingerprint, inline or embed CSS and JavaScript assets.</p>\n<h2 id=\"fingerprint\">fingerprint</h2>\n<p>Add the file content finger print to the file name.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(path)|fingerprint }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ path|fingerprint }}</span></code></pre>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset('styles.css')|fingerprint }}</span></code></pre>\n<h2 id=\"minify\">minify</h2>\n<p>Minifying a CSS or a JavaScript file.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(path)|minify }}</span></code></pre>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset('styles.css')|minify }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ asset('scripts.js')|minify }}</span></code></pre>\n<h2 id=\"minify-css\">minify_css</h2>\n<p>Minifying a CSS string.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ variable|minify_css }}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">apply</span></span> minify_css %}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# CSS here #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endapply</span></span> %}</span></code></pre>\n<p><em>Examples:</em></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 = 'some CSS here' %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ styles|minify_css }}</span></code></pre>\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> minify_css %}</span><span class=\"xml\"><span class=\"css\">\n  <span class=\"hljs-selector-tag\">html</span> {\n    <span class=\"hljs-attribute\">background-color</span>: <span class=\"hljs-number\">#fcfcfc</span>;\n    <span class=\"hljs-attribute\">color</span>: <span class=\"hljs-number\">#444</span>;\n  }\n</span></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<h2 id=\"minify-js\">minify_js</h2>\n<p>Minifying a JavaScript string.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ variable|minify_js }}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">apply</span></span> minify_js %}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# JavaScript here #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endapply</span></span> %}</span></code></pre>\n<p><em>Examples:</em></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> script = 'some JavaScript here' %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ script|minify_js }}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">apply</span></span> minify_js %}</span><span class=\"xml\"><span class=\"javascript\">\n  <span class=\"hljs-keyword\">var</span> test = <span class=\"hljs-string\">'test'</span>;\n  <span class=\"hljs-built_in\">console</span>.log(test);\n</span></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\">script</span>&gt;</span></span></code></pre>\n<h2 id=\"scss-to-css\">scss_to_css</h2>\n<p>Compiles a <a href=\"https://sass-lang.com\" target=\"_blank\" rel=\"noopener noreferrer\">Sass</a> string to CSS.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ variable|scss_to_css }}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">apply</span></span> scss_to_css %}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# SCSS here #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endapply</span></span> %}</span></code></pre>\n<p>Alias: <code translate=\"no\">sass_to_css</code>.</p>\n<p><em>Examples:</em></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> scss = 'some SCSS here' %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ scss|scss_to_css }}</span></code></pre>\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 %}</span><span class=\"xml\">\n  $color: #fcfcfc;\n  div {\n    color: lighten($color, 20%);\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<h2 id=\"to-css\">to_css</h2>\n<p>Compiles a <a href=\"https://sass-lang.com\" target=\"_blank\" rel=\"noopener noreferrer\">Sass</a> file to CSS.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(path)|to_css }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ path|to_css }}</span></code></pre>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset('styles.scss')|to_css }}</span></code></pre>\n<h2 id=\"inline\">inline</h2>\n<p>Outputs the content of an <em>Asset</em>.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(path)|inline }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset('styles.css')|inline }}</span></code></pre>\n<h2 id=\"dataurl\">dataurl</h2>\n<p>Returns the <a href=\"https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs\" target=\"_blank\" rel=\"noopener noreferrer\">data URL</a> of an asset.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset(path)|dataurl }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ asset(image_path)|dataurl }}</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/languages/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/languages/",
      "title": "Languages",
      "summary": "Main language and additional languages of a multilingual website.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Languages\nThese options define the main language of the site and, for a multilingual website, its additional languages.\nlanguage\nThe main language, defined by its code.\nlanguage: &lt;code&gt; # unique code (`en` by default)\nBy default only others languages pages path are prefixed with its language code, but you can prefix the path of the main language pages with the following option:\n#language: &lt;code&gt;\nlanguage:\n  code: &lt;code&gt;\n  prefix: true\nWhen prefix is set to true, an alias is automatically created for the home page that redirect from\/ to \/&lt;code&gt;\/.\nlanguages\nOptions of available languages, used for pages and templates localization.\nlanguages:\n  - code: &lt;code&gt;          # unique code (e.g.: `en`, `fr`, 'en-US', `fr-CA`)\n    name: &lt;name&gt;          # human readable name (e.g.: `Français`)\n    locale: &lt;locale&gt;      # locale code (`language_COUNTRY`, e.g.: `en_US`, `fr_FR`, `fr_CA`)\n    enabled: &lt;true|false&gt; # enabled or not (`true` by default)\nExample:\nlanguage: en\nlanguages:\n  - code: en\n    name: English\n    locale: en_US\n  - code: fr\n    name: Français\n    locale: fr_FR\nA locale code list is available if needed.\nLocalize\nTo localize configuration options you must store them under the config key of the language.\nExample:\ntitle: \"Cecil in english\"\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: \"Cecil en français\"\nIn templates you can access to an option with {{ site.&lt;option&gt; }}, for example {{ site.title }}.\nIf an option is not available in the current language (e.g.: fr) it fallback to the global one (e.g.: en).",
      "content_html": "<h1>Languages</h1>\n<p>These options define the main language of the site and, for a multilingual website, its additional languages.</p>\n<h2 id=\"language\">language</h2>\n<p>The main language, defined by its code.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">language:</span> <span class=\"hljs-string\">&lt;code&gt;</span> <span class=\"hljs-comment\"># unique code (`en` by default)</span></code></pre>\n<p>By default only others <a href=\"#languages\">languages</a> pages path are prefixed with its language code, but you can prefix the path of the main language pages with the following option:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-comment\">#language: &lt;code&gt;</span>\n<span class=\"hljs-attr\">language:</span>\n  <span class=\"hljs-attr\">code:</span> <span class=\"hljs-string\">&lt;code&gt;</span>\n  <span class=\"hljs-attr\">prefix:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<aside class=\"note note-info\"><p>When <code translate=\"no\">prefix</code> is set to <code translate=\"no\">true</code>, an alias is automatically created for the home page that redirect from<code translate=\"no\">/</code> to <code translate=\"no\">/&lt;code&gt;/</code>.</p></aside>\n<h2 id=\"languages\">languages</h2>\n<p>Options of available languages, used for <a href=\"../content/4-multilingual.md\">pages</a> and <a href=\"../templates/5-localization.md\">templates</a> localization.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">languages:</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">code:</span> <span class=\"hljs-string\">&lt;code&gt;</span>          <span class=\"hljs-comment\"># unique code (e.g.: `en`, `fr`, 'en-US', `fr-CA`)</span>\n    <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">&lt;name&gt;</span>          <span class=\"hljs-comment\"># human readable name (e.g.: `Français`)</span>\n    <span class=\"hljs-attr\">locale:</span> <span class=\"hljs-string\">&lt;locale&gt;</span>      <span class=\"hljs-comment\"># locale code (`language_COUNTRY`, e.g.: `en_US`, `fr_FR`, `fr_CA`)</span>\n    <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-string\">&lt;true|false&gt;</span> <span class=\"hljs-comment\"># enabled or not (`true` by default)</span></code></pre>\n<p><em>Example:</em></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></code></pre>\n<aside class=\"note note-info\"><p>A <a href=\"3-locale-codes.md\">locale code list</a> is available if needed.</p></aside>\n<h3 id=\"localize\">Localize</h3>\n<p>To localize configuration options you must store them under the <code translate=\"no\">config</code> key of the language.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">\"Cecil in english\"</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\">\"Cecil en français\"</span></code></pre>\n<aside class=\"note note-info\"><p>In <a href=\"../templates/index.md\">templates</a> you can access to an option with <code translate=\"no\">{{ site.&lt;option&gt; }}</code>, for example <code translate=\"no\">{{ site.title }}</code>.<br>\nIf an option is not available in the current language (e.g.: <code translate=\"no\">fr</code>) it fallback to the global one (e.g.: <code translate=\"no\">en</code>).</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/content/front-matter/",
      "url": "https://cecil-945.pages.dev/documentation/content/front-matter/",
      "title": "Front matter",
      "summary": "Custom and predefined page variables: menu, taxonomy, schedule, redirect, alias, output, etc.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-03T00:00:00+00:00","content_text": "Front matter\nThe front matter can contains custom variables applied to the current page.\nIt must be the first thing in the file and must be a valid YAML.\nPredefined variables\n\n\n\nVariable\nDescription\nDefault value\nExample\n\n\n\n\ntitle\nTitle\nFile name without extension.\nPost 1\n\n\nlayout\nTemplate\nSee Lookup rules.\n404\n\n\ndate\nCreation date\nFile creation date (PHP DateTime object).\n2019\/04\/15\n\n\nsection\nSection\nPage's Section.\nblog\n\n\npath\nPath\nPage's path.\nblog\/post-1\n\n\nslug\nSlug\nPage's slug.\npost-1\n\n\npublished\nPublished or not\ntrue.\nfalse\n\n\ndraft\nPublished or not\nfalse.\ntrue\n\n\n\nAll the predefined variables can be overridden except section.\nupdated\nThe updated variable is used to define the last modification date of a page.\nExample:\n---\nupdated: 2026-02-02\n---\nBefore version 8.80.1, the updated variable was a predefined variable. It is now an optional variable (and must be defined in the front matter to be used).\nmenu\nA page can be added to a menu.\nThe entry name is the page title and the URL is the page path.\nThe same page can be added to multiple menus, and each entry's position can be set with the weight key (lowest first). The name key can be used to override the default entry name per menu.\nExamples:\n---\nmenu: main\n---\n---\nmenu: [main, navigation] # same page in multiple menus\n---\n---\nmenu:\n  main:\n    weight: 10\n  navigation:\n    weight: 20\n---\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---\nTaxonomy\nTaxonomy allows you to connect, relate and classify your website’s content.\nIn Cecil, these terms are gathered within vocabularies.\nVocabularies are declared in the Configuration.\n\nVocabulary\nA categorization of content (e.g.: tags, categories, etc.).\nTerm\nA term is an item of a vocabulary (e.g.: Development, PHP, etc.).\n\nExample:\n---\ntags: [\"Development\", \"PHP\"]\n---\nCecil then generates, for each vocabulary:\n\na page listing its terms, e.g.: \/tags\/\na page per term listing its pages, e.g.: \/tags\/development\/ and \/tags\/php\/\n\nSee templates lookup rules and taxonomy variables to customize those pages.\nSchedule\nSchedules pages’ publication.\nExample:\nThe page will be published if current date is &gt;= 2023-02-07:\nschedule:\n  publish: 2023-02-07\nThis page is published if current date is &lt;= 2022-04-28:\nschedule:\n  expiry: 2022-04-28\nredirect\nAs indicated by its name, the redirect variable is used to redirect a page to a dedicated URL.\nExample:\n---\nredirect: \"https:\/\/arnaudligny.fr\"\n---\nRedirect works with the redirect.html.twig template.\nalias\nAlias is a redirection to the current page\nExample:\n---\ntitle: \"About\"\nalias:\n  - contact\n---\nIn the previous example contact\/ redirects to about\/.\noutput\nDefines the output format of the page.\nAvailable formats are: html, atom, rss, json, xml, etc.\nYou can define one or more formats in an array.\nI’s not required to define an output format, but if you do, it must be one of the available formats defined in the Configuration.\nExample:\n---\noutput: [html, atom]\n---\nexternal\nA page with an external variable try to fetch the content of the pointed resource.\nExample:\n---\nexternal: \"https:\/\/raw.githubusercontent.com\/Cecilapp\/Cecil\/main\/README.md\"\n---\nexcluded\nSet excluded to true to hide a page from list pages (i.e.: Home page, Section, Sitemap, etc.).\nExample:\n---\nexcluded: true\n---\nexcluded is different from published: an excluded page is published but hidden from list pages.\nSince version 8.49.0, the previous exclude variable have been changed to excluded.",
      "content_html": "<h1>Front matter</h1>\n<p>The <em>front matter</em> can contains custom variables applied to the current page.</p>\n<p>It must be the first thing in the file and must be a valid <a href=\"https://en.wikipedia.org/wiki/YAML\" target=\"_blank\" rel=\"noopener noreferrer\">YAML</a>.</p>\n<h2 id=\"predefined-variables\">Predefined variables</h2>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n<th>Default value</th>\n<th>Example</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">title</code></td>\n<td>Title</td>\n<td>File name without extension.</td>\n<td><code translate=\"no\">Post 1</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">layout</code></td>\n<td>Template</td>\n<td>See <a href=\"../templates/1-lookup-rules.md#lookup-rules\"><em>Lookup rules</em></a>.</td>\n<td><code translate=\"no\">404</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">date</code></td>\n<td>Creation date</td>\n<td>File creation date (PHP <em>DateTime</em> object).</td>\n<td><code translate=\"no\">2019/04/15</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">section</code></td>\n<td>Section</td>\n<td>Page's <em>Section</em>.</td>\n<td><code translate=\"no\">blog</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">path</code></td>\n<td>Path</td>\n<td>Page's <em>path</em>.</td>\n<td><code translate=\"no\">blog/post-1</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">slug</code></td>\n<td>Slug</td>\n<td>Page's <em>slug</em>.</td>\n<td><code translate=\"no\">post-1</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">published</code></td>\n<td>Published or not</td>\n<td><code translate=\"no\">true</code>.</td>\n<td><code translate=\"no\">false</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">draft</code></td>\n<td>Published or not</td>\n<td><code translate=\"no\">false</code>.</td>\n<td><code translate=\"no\">true</code></td>\n</tr>\n</tbody>\n</table>\n<aside class=\"note note-info\"><p>All the predefined variables can be overridden except <code translate=\"no\">section</code>.</p></aside>\n<h2 id=\"updated\">updated</h2>\n<p>The <code translate=\"no\">updated</code> variable is used to define the last modification date of a page.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">updated:</span> <span class=\"hljs-number\">2026</span><span class=\"hljs-number\">-02</span><span class=\"hljs-number\">-02</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<aside class=\"note note-warning\"><p>Before version 8.80.1, the <code translate=\"no\">updated</code> variable was a predefined variable. It is now an optional variable (and must be defined in the front matter to be used).</p></aside>\n<h2 id=\"menu\">menu</h2>\n<p>A page can be added to a <a href=\"../configuration/1-site.md#menus\">menu</a>.</p>\n<p>The entry name is the page <code translate=\"no\">title</code> and the URL is the page <code translate=\"no\">path</code>.</p>\n<p>The same page can be added to multiple menus, and each entry's position can be set with the <code translate=\"no\">weight</code> key (lowest first). The <code translate=\"no\">name</code> key can be used to override the default entry name per menu.</p>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">menu:</span> <span class=\"hljs-string\">main</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">menu:</span> <span class=\"hljs-string\">[main,</span> <span class=\"hljs-string\">navigation]</span> <span class=\"hljs-comment\"># same page in multiple menus</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</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\">10</span>\n  <span class=\"hljs-attr\">navigation:</span>\n    <span class=\"hljs-attr\">weight:</span> <span class=\"hljs-number\">20</span>\n<span class=\"hljs-meta\">---</span></code></pre>\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 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=\"taxonomy\">Taxonomy</h2>\n<p>Taxonomy allows you to connect, relate and classify your website’s content.<br>\nIn Cecil, these terms are gathered within vocabularies.</p>\n<p>Vocabularies are declared in the <a href=\"../configuration/1-site.md#taxonomies\"><em>Configuration</em></a>.</p>\n<dl>\n<dt>Vocabulary</dt>\n<dd>A categorization of content (e.g.: <code translate=\"no\">tags</code>, <code translate=\"no\">categories</code>, etc.).</dd>\n<dt>Term</dt>\n<dd>A term is an item of a vocabulary (e.g.: <code translate=\"no\">Development</code>, <code translate=\"no\">PHP</code>, etc.).</dd>\n</dl>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">tags:</span> <span class=\"hljs-string\">[\"Development\",</span> <span class=\"hljs-string\">\"PHP\"</span><span class=\"hljs-string\">]</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<p>Cecil then generates, for each vocabulary:</p>\n<ul>\n<li>a page listing its terms, e.g.: <code translate=\"no\">/tags/</code></li>\n<li>a page per term listing its pages, e.g.: <code translate=\"no\">/tags/development/</code> and <code translate=\"no\">/tags/php/</code></li>\n</ul>\n<p>See <a href=\"../templates/1-lookup-rules.md#type-vocabulary\">templates lookup rules</a> and <a href=\"../templates/2-variables.md#taxonomy\">taxonomy variables</a> to customize those pages.</p>\n<h2 id=\"schedule\">Schedule</h2>\n<p>Schedules pages’ publication.</p>\n<p><em>Example:</em></p>\n<p>The page will be published if current date is &gt;= 2023-02-07:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">schedule:</span>\n  <span class=\"hljs-attr\">publish:</span> <span class=\"hljs-number\">2023</span><span class=\"hljs-number\">-02</span><span class=\"hljs-number\">-07</span></code></pre>\n<p>This page is published if current date is &lt;= 2022-04-28:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">schedule:</span>\n  <span class=\"hljs-attr\">expiry:</span> <span class=\"hljs-number\">2022</span><span class=\"hljs-number\">-04</span><span class=\"hljs-number\">-28</span></code></pre>\n<h2 id=\"redirect\">redirect</h2>\n<p>As indicated by its name, the <code translate=\"no\">redirect</code> variable is used to redirect a page to a dedicated URL.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">redirect:</span> <span class=\"hljs-string\">\"https://arnaudligny.fr\"</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<aside class=\"note note-info\"><p>Redirect works with the <a href=\"https://github.com/Cecilapp/Cecil/blob/main/resources/layouts/_default/redirect.html.twig\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">redirect.html.twig</code></a> template.</p></aside>\n<h2 id=\"alias\">alias</h2>\n<p>Alias is a redirection to the current page</p>\n<p><em>Example:</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\">\"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-meta\">---</span></code></pre>\n<p>In the previous example <code translate=\"no\">contact/</code> redirects to <code translate=\"no\">about/</code>.</p>\n<h2 id=\"output\">output</h2>\n<p>Defines the output format of the page.</p>\n<p>Available formats are: <code translate=\"no\">html</code>, <code translate=\"no\">atom</code>, <code translate=\"no\">rss</code>, <code translate=\"no\">json</code>, <code translate=\"no\">xml</code>, etc.<br>\nYou can define one or more formats in an array.</p>\n<p>I’s not required to define an output format, but if you do, it must be one of the available formats defined in the <a href=\"../configuration/8-output.md#output-formats\"><em>Configuration</em></a>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">[html,</span> <span class=\"hljs-string\">atom]</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<h2 id=\"external\">external</h2>\n<p>A page with an <code translate=\"no\">external</code> variable try to fetch the content of the pointed resource.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">external:</span> <span class=\"hljs-string\">\"https://raw.githubusercontent.com/Cecilapp/Cecil/main/README.md\"</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<h2 id=\"excluded\">excluded</h2>\n<p>Set <code translate=\"no\">excluded</code> to <code translate=\"no\">true</code> to hide a page from list pages (i.e.: <em>Home page</em>, <em>Section</em>, <em>Sitemap</em>, etc.).</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">excluded:</span> <span class=\"hljs-literal\">true</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<aside class=\"note note-info\"><p><code translate=\"no\">excluded</code> is different from <a href=\"#predefined-variables\"><code translate=\"no\">published</code></a>: an excluded page is published but hidden from list pages.</p></aside>\n<aside class=\"note note-warning\"><p>Since version 8.49.0, the previous <code translate=\"no\">exclude</code> variable have been changed to <code translate=\"no\">excluded</code>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/templates/variables/",
      "url": "https://cecil-945.pages.dev/documentation/templates/variables/",
      "title": "Variables",
      "summary": "Variables available in templates: site, page and cecil.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-06T00:00:00+00:00","content_text": "Variables\n\nThe application passes variables to the templates for manipulation in the template. Variables may have attributes or elements you can access, too.\nUse a dot (.) to access attributes of a variable: {{ foo.bar }}\n\nYou can use variables from different scopes: site, page, cecil.\nsite\nThe site variable contains built-in variables and those set in the configuration.\n\n\n\nVariable\nDescription\n\n\n\n\nsite.pages\nCollection of all pages, in the current language.\n\n\nsite.allpages\nCollection of all pages, in all languages.\n\n\nsite.page(id)\nA page with the given ID.\n\n\nsite.taxonomies\nCollection of vocabularies.\n\n\nsite.home\nID of the home page.\n\n\nsite.time\nCurrent Timestamp.\n\n\nsite.debug\nDebug mode status (true or false).\n\n\nsite.build\nCurrent build ID.\n\n\n\nExample:\ntitle: \"My amazing website!\"\nCan be displayed in a template with:\n{{ site.title }}\nUse showable method on pages collection to return only published and not virtual\/redirect\/excluded pages.\nExample:\n{% for page in site.pages.showable %}\n  &lt;a href=\"{{ url(page) }}\"&gt;{{ page.title }}&lt;\/a&gt;\n{% endfor %}\nIn some cases, you can encounter conflicts between configuration and built-in variables (e.g. pages.default configuration). In that case, you can use config.&lt;variable&gt; (where &lt;variable&gt; is the variable name\/path) to access the raw configuration directly.\nExample:\n{{ config.pages.default.sitemap.priority }}\nsite.menus\nLoop on site.menus.&lt;menu&gt; to get each entry of the &lt;menu&gt; collection (e.g.: main).\n\n\n\nVariable\nDescription\n\n\n\n\n&lt;entry&gt;.name\nEntry name.\n\n\n&lt;entry&gt;.url\nEntry URL.\n\n\n&lt;entry&gt;.weight\nEntry weight (useful to sort menu entries).\n\n\n\nExample:\n&lt;nav&gt;\n  &lt;ol&gt;\n  {% for entry in site.menus.main|sort_by_weight %}\n    &lt;li&gt;&lt;a href=\"{{ url(entry.url) }}\" data-weight=\"{{ entry.weight }}\"&gt;{{ entry.name }}&lt;\/a&gt;&lt;\/li&gt;\n  {% endfor %}\n  &lt;\/ol&gt;\n&lt;\/nav&gt;\nsite.language\nInformation about the current language.\n\n\n\nVariable\nDescription\n\n\n\n\nsite.language\nLanguage code (e.g.: en).\n\n\nsite.language.name\nLanguage name (e.g.: English).\n\n\nsite.language.locale\nLanguage locale code (e.g.: en_US).\n\n\nsite.language.weight\nLanguage position in the languages list.\n\n\n\nYou can retrieve name, locale and weight of a specific language by passing its code as a parameter.\ne.g.: site.language.name('fr').\nsite.static\nThe static files collection can be accessed via site.static if the static load is enabled.\nEach file exposes the following properties:\n\npath: relative path (e.g.: \/images\/img-1.jpg)\ndate: creation date (timestamp)\nupdated: modification date (timestamp)\nname: name (e.g.: img-1.jpg)\nbasename: name without extension (e.g.: img-1)\next: extension (e.g.: jpg)\ntype: media type (e.g.: image)\nsubtype: media sub type (e.g.: image\/jpeg)\nexif: image EXIF data (array)\naudio: Mp3Info object\nvideo: array of basic video information (duration in seconds, width and height)\n\nsite.data\nA data collection can be accessed via site.data.&lt;filename&gt; (without file extension).\nExamples:\n\ndata\/authors.yml : site.data.authors\ndata\/authors.fr.yml : site.data.authors (if site.language = \"fr\")\ndata\/galleries\/gallery-1.json : site.data.galleries['gallery-1']\n\npage\nThe page variable contains built-in variables of a page and those set in the front matter.\n\n\n\nVariable\nDescription\nExample\n\n\n\n\npage.id\nUnique identifier.\nblog\/post-1\n\n\npage.title\nFile name (without extension).\nPost 1\n\n\npage.date\nFile creation date.\nDateTime\n\n\npage.body\nFile body.\nMarkdown\n\n\npage.content\nFile body converted in HTML.\nHTML\n\n\npage.section\nFile root folder (slugified).\nblog\n\n\npage.path\nFile path (slugified).\nblog\/post-1\n\n\npage.slug\nFile name (slugified).\npost-1\n\n\npage.filepath\nFile system path.\nBlog\/Post 1.md\n\n\npage.type\nhomepage, page, section, vocabulary or term.\npage\n\n\npage.pages\nCollection of all sub pages.\nCollection\n\n\npage.translations\nCollection of translated pages.\nCollection\n\n\n\nUse showable method on pages collection to return only published and not virtual\/redirect\/excluded pages.\nExample:\n{% for page in page.pages.showable %}\n  &lt;a href=\"{{ url(page) }}\"&gt;{{ page.title }}&lt;\/a&gt;\n{% endfor %}\nNested sections\nIn a nested sections context, page.parent, page.ancestors, page.sections and page.toplevel help you build navigation.\n\n\n\nVariable\nDescription\nExample\n\n\n\n\npage.parent\nParent section's page (null if none).\nPage\n\n\npage.ancestors\nCollection of ancestor sections (nearest first).\nCollection\n\n\npage.sections\nCollection of immediate descendant sections.\nCollection\n\n\npage.toplevel\ntrue if the page is a top level section.\nBoolean\n\n\n\nBreadcrumb (from the home page to the current page):\n&lt;nav aria-label=\"breadcrumb\"&gt;\n  &lt;ul&gt;\n    &lt;li&gt;&lt;a href=\"{{ url(site.home) }}\"&gt;{{ site.title }}&lt;\/a&gt;&lt;\/li&gt;\n    {% for section in page.ancestors|reverse %}\n    &lt;li&gt;&lt;a href=\"{{ url(section) }}\"&gt;{{ section.title }}&lt;\/a&gt;&lt;\/li&gt;\n    {% endfor %}\n    {% if page.id != site.home %}\n    &lt;li&gt;&lt;a href=\"{{ url(page) }}\" aria-current=\"page\"&gt;{{ page.title }}&lt;\/a&gt;&lt;\/li&gt;\n    {% endif %}\n  &lt;\/ul&gt;\n&lt;\/nav&gt;\nA ready-to-use breadcrumb.html.twig partial is available:\n{{ include('partials\/breadcrumb.html.twig') }}\nSub-sections menu (immediate descendant sections of the current section):\n{% if page.sections|length %}\n&lt;ul&gt;\n  {% for section in page.sections|sort_by_title %}\n  &lt;li&gt;&lt;a href=\"{{ url(section) }}\"&gt;{{ section.title }}&lt;\/a&gt;&lt;\/li&gt;\n  {% endfor %}\n&lt;\/ul&gt;\n{% endif %}\nMain navigation limited to top level sections (from any page):\n&lt;nav&gt;\n  {% for section in site.page(site.home).sections|sort_by_title %}\n  &lt;a href=\"{{ url(section) }}\"&gt;{{ section.title }}&lt;\/a&gt;\n  {% endfor %}\n&lt;\/nav&gt;\nLink to the parent section:\n{% if page.parent %}\n&lt;a href=\"{{ url(page.parent) }}\"&gt;← {{ page.parent.title }}&lt;\/a&gt;\n{% endif %}\npage.&lt;prev\/next&gt;\nNavigation between pages within the same section, sorted according to the section's sortby (chronological order for dates).\nWith sub-sections, navigation follows the sections tree: the pages of a top level Section and of all its sub-sections are chained, each sub-section (its index page) being placed among the pages of its parent Section and followed by its own pages.\n\n\n\nVariable\nDescription\nExample\n\n\n\n\npage.prev\nPrevious page.\nPage\n\n\npage.next\nNext page.\nPage\n\n\n\nExample:\n&lt;a href=\"{{ url(page.prev) }}\"&gt;{{ page.prev.title }}&lt;\/a&gt;\npage.paginator\nPaginator helps you build navigation for list pages: homepage, sections, and taxonomies.\n\n\n\nVariable\nDescription\n\n\n\n\npage.paginator.pages\nPages Collection.\n\n\npage.paginator.pages_total\nNumber total of pages.\n\n\npage.paginator.count\nNumber of paginator's pages.\n\n\npage.paginator.current\nPosition index of the current page.\n\n\npage.paginator.links.first\nPage ID of the first page.\n\n\npage.paginator.links.prev\nPage ID of the previous page.\n\n\npage.paginator.links.self\nPage ID of the current page.\n\n\npage.paginator.links.next\nPage ID of the next page.\n\n\npage.paginator.links.last\nPage ID of the last page.\n\n\npage.paginator.links.path\nPage ID without the position index.\n\n\n\nBecause links entries are Page ID you must use the url() function to create working links.\ne.g: {{ url(page.paginator.links.next) }}\nExample:\n{% if page.paginator %}\n&lt;div&gt;\n  {% if page.paginator.links.prev is defined %}\n  &lt;a href=\"{{ url(page.paginator.links.prev) }}\"&gt;Previous&lt;\/a&gt;\n  {% endif %}\n  {% if page.paginator.links.next is defined %}\n  &lt;a href=\"{{ url(page.paginator.links.next) }}\"&gt;Next&lt;\/a&gt;\n  {% endif %}\n&lt;\/div&gt;\n{% endif %}\nExample:\n{% if page.paginator %}\n&lt;div&gt;\n  {% for paginator_index in 1..page.paginator.count %}\n    {% if paginator_index != page.paginator.current %}\n      {% if paginator_index == 1 %}\n  &lt;a href=\"{{ url(page.paginator.links.first) }}\"&gt;{{ paginator_index }}&lt;\/a&gt;\n      {% else %}\n  &lt;a href=\"{{ url(page.paginator.links.path ~ '\/' ~ paginator_index) }}\"&gt;{{ paginator_index }}&lt;\/a&gt;\n      {% endif %}\n    {% else %}\n  {{ paginator_index }}\n    {% endif %}\n  {% endfor %}\n&lt;\/div&gt;\n{% endif %}\nTaxonomy\nVariables available in vocabulary and term templates.\nVocabulary\nPage \/&lt;plural&gt;\/ (e.g.: \/categories\/).\n\n\n\nVariable\nDescription\n\n\n\n\npage.plural\nVocabulary name in plural form.\n\n\npage.singular\nVocabulary name in singular form.\n\n\npage.terms\nList of terms (Collection).\n\n\n\nEach term of page.terms provides term.id (term ID, e.g.: categories\/php), term.name (term name, e.g.: PHP) and the number of its pages with term|length.\nTerm\nPage \/&lt;plural&gt;\/&lt;term&gt;\/ (e.g.: \/categories\/php\/).\n\n\n\nVariable\nDescription\n\n\n\n\npage.title\nTerm name.\n\n\npage.term\nTerm ID (e.g.: categories\/php).\n\n\npage.plural\nVocabulary name in plural form.\n\n\npage.singular\nVocabulary name in singular form.\n\n\npage.pages\nList of pages in this term, sorted by date (Collection).\n\n\n\nTaxonomy example\nConfiguration:\ntaxonomies:\n  categories: category\nPage front matter:\n---\ncategories: [\"Data Sovereignty\"]\n---\nList of terms (\/categories\/), in layouts\/taxonomy\/categories.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 %}\nList of pages of a term (\/categories\/data-sovereignty\/), in layouts\/taxonomy\/category.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 %}\nLinks to the terms of the current page, in a page template:\n{% for category in page.categories ?? [] %}\n  &lt;a href=\"{{ url('categories\/' ~ category) }}\"&gt;{{ category }}&lt;\/a&gt;\n{% endfor %}\nThe url() function slugifies the given string to find the matching page: url('categories\/Data Sovereignty') returns \/categories\/data-sovereignty\/.\nYou can also use the built-in partial {{ include('partials\/terms-list.html.twig', {vocabulary: 'categories'}) }}.\ncecil\n\n\n\nVariable\nDescription\n\n\n\n\ncecil.url\nURL of the Cecil website.\n\n\ncecil.version\nCecil current version.\n\n\ncecil.poweredby\nPrint Cecil v%s, with %s is the current version.\n\n\n",
      "content_html": "<h1>Variables</h1>\n<blockquote>\n<p>The application passes variables to the templates for manipulation in the template. Variables may have attributes or elements you can access, too.<br>\nUse a dot (.) to access attributes of a variable: <code translate=\"no\">{{ foo.bar }}</code></p>\n</blockquote>\n<p>You can use variables from different scopes: <a href=\"#site\"><code translate=\"no\">site</code></a>, <a href=\"#page\"><code translate=\"no\">page</code></a>, <a href=\"#cecil\"><code translate=\"no\">cecil</code></a>.</p>\n<h2 id=\"site\">site</h2>\n<p>The <code translate=\"no\">site</code> variable contains built-in variables <strong>and</strong> those set in the <a href=\"../configuration/index.md\">configuration</a>.</p>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">site.pages</code></td>\n<td>Collection of all pages, in the current language.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.allpages</code></td>\n<td>Collection of all pages, in all languages.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.page(id)</code></td>\n<td>A page with the given ID.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.taxonomies</code></td>\n<td>Collection of vocabularies.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.home</code></td>\n<td>ID of the home page.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.time</code></td>\n<td>Current <a href=\"https://wikipedia.org/wiki/Unix_time\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Timestamp</em></a>.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.debug</code></td>\n<td>Debug mode status (<code translate=\"no\">true</code> or <code translate=\"no\">false</code>).</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.build</code></td>\n<td>Current build ID.</td>\n</tr>\n</tbody>\n</table>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">\"My amazing website!\"</span></code></pre>\n<p>Can be displayed in a template with:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ site.title }}</span></code></pre>\n<aside class=\"note note-important\"><p>Use <code translate=\"no\">showable</code> method on pages collection to return only published and not <em>virtual/redirect/excluded</em> pages.</p>\n<p><em>Example:</em></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> page in site.pages.showable %}</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) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ page.title }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span></code></pre></aside>\n<aside class=\"note note-warning\"><p>In some cases, you can encounter conflicts between configuration and built-in variables (e.g. <code translate=\"no\">pages.default</code> configuration). In that case, you can use <code translate=\"no\">config.&lt;variable&gt;</code> (where <code translate=\"no\">&lt;variable&gt;</code> is the variable name/path) to access the raw configuration directly.</p>\n<p>Example:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ config.pages.default.sitemap.priority }}</span></code></pre></aside>\n<h3 id=\"site-menus\">site.menus</h3>\n<p>Loop on <code translate=\"no\">site.menus.&lt;menu&gt;</code> to get each entry of the <code translate=\"no\">&lt;menu&gt;</code> collection (e.g.: <code translate=\"no\">main</code>).</p>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">&lt;entry&gt;.name</code></td>\n<td>Entry name.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">&lt;entry&gt;.url</code></td>\n<td>Entry URL.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">&lt;entry&gt;.weight</code></td>\n<td>Entry weight (useful to sort menu entries).</td>\n</tr>\n</tbody>\n</table>\n<p><em>Example:</em></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\">ol</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><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a</span> <span class=\"hljs-attr\">href</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ url(entry.url) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">data-weight</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ entry.weight }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ entry.name }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">li</span>&gt;</span>\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\">ol</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">nav</span>&gt;</span></span></code></pre>\n<h3 id=\"site-language\">site.language</h3>\n<p>Information about the current language.</p>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">site.language</code></td>\n<td>Language code (e.g.: <code translate=\"no\">en</code>).</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.language.name</code></td>\n<td>Language name (e.g.: <code translate=\"no\">English</code>).</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.language.locale</code></td>\n<td>Language <a href=\"../configuration/3-locale-codes.md\">locale code</a> (e.g.: <code translate=\"no\">en_US</code>).</td>\n</tr>\n<tr>\n<td><code translate=\"no\">site.language.weight</code></td>\n<td>Language position in the <code translate=\"no\">languages</code> list.</td>\n</tr>\n</tbody>\n</table>\n<aside class=\"note note-tip\"><p>You can retrieve <code translate=\"no\">name</code>, <code translate=\"no\">locale</code> and <code translate=\"no\">weight</code> of a specific language by passing its code as a parameter.<br>\ne.g.: <code translate=\"no\">site.language.name('fr')</code>.</p></aside>\n<h3 id=\"site-static\">site.static</h3>\n<p>The static files collection can be accessed via <code translate=\"no\">site.static</code> if the <a href=\"../configuration/5-data-static.md#static-load\"><em>static load</em></a> is enabled.</p>\n<p>Each file exposes the following properties:</p>\n<ul>\n<li><code translate=\"no\">path</code>: relative path (e.g.: <code translate=\"no\">/images/img-1.jpg</code>)</li>\n<li><code translate=\"no\">date</code>: creation date (<em>timestamp</em>)</li>\n<li><code translate=\"no\">updated</code>: modification date (<em>timestamp</em>)</li>\n<li><code translate=\"no\">name</code>: name (e.g.: <code translate=\"no\">img-1.jpg</code>)</li>\n<li><code translate=\"no\">basename</code>: name without extension (e.g.: <code translate=\"no\">img-1</code>)</li>\n<li><code translate=\"no\">ext</code>: extension (e.g.: <code translate=\"no\">jpg</code>)</li>\n<li><code translate=\"no\">type</code>: media type (e.g.: <code translate=\"no\">image</code>)</li>\n<li><code translate=\"no\">subtype</code>: media sub type (e.g.: <code translate=\"no\">image/jpeg</code>)</li>\n<li><code translate=\"no\">exif</code>: image EXIF data (<em>array</em>)</li>\n<li><code translate=\"no\">audio</code>: <a href=\"https://github.com/wapmorgan/Mp3Info#audio-information\" target=\"_blank\" rel=\"noopener noreferrer\">Mp3Info</a> object</li>\n<li><code translate=\"no\">video</code>: array of basic video information (duration in seconds, width and height)</li>\n</ul>\n<h3 id=\"site-data\">site.data</h3>\n<p>A data collection can be accessed via <code translate=\"no\">site.data.&lt;filename&gt;</code> (without file extension).</p>\n<p><em>Examples:</em></p>\n<ul>\n<li><code translate=\"no\">data/authors.yml</code> : <code translate=\"no\">site.data.authors</code></li>\n<li><code translate=\"no\">data/authors.fr.yml</code> : <code translate=\"no\">site.data.authors</code> (if <code translate=\"no\">site.language</code> = \"fr\")</li>\n<li><code translate=\"no\">data/galleries/gallery-1.json</code> : <code translate=\"no\">site.data.galleries['gallery-1']</code></li>\n</ul>\n<h2 id=\"page\">page</h2>\n<p>The <code translate=\"no\">page</code> variable contains built-in variables of a page <strong>and</strong> those set in the <a href=\"../content/1-pages.md#front-matter\">front matter</a>.</p>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n<th>Example</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">page.id</code></td>\n<td>Unique identifier.</td>\n<td><code translate=\"no\">blog/post-1</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.title</code></td>\n<td>File name (without extension).</td>\n<td><code translate=\"no\">Post 1</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.date</code></td>\n<td>File creation date.</td>\n<td><em>DateTime</em></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.body</code></td>\n<td>File body.</td>\n<td><em>Markdown</em></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.content</code></td>\n<td>File body converted in HTML.</td>\n<td><em>HTML</em></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.section</code></td>\n<td>File root folder (<em>slugified</em>).</td>\n<td><code translate=\"no\">blog</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.path</code></td>\n<td>File path (<em>slugified</em>).</td>\n<td><code translate=\"no\">blog/post-1</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.slug</code></td>\n<td>File name (<em>slugified</em>).</td>\n<td><code translate=\"no\">post-1</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.filepath</code></td>\n<td>File system path.</td>\n<td><code translate=\"no\">Blog/Post 1.md</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.type</code></td>\n<td><code translate=\"no\">homepage</code>, <code translate=\"no\">page</code>, <code translate=\"no\">section</code>, <code translate=\"no\">vocabulary</code> or <code translate=\"no\">term</code>.</td>\n<td><code translate=\"no\">page</code></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.pages</code></td>\n<td>Collection of all sub pages.</td>\n<td><em>Collection</em></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.translations</code></td>\n<td>Collection of translated pages.</td>\n<td><em>Collection</em></td>\n</tr>\n</tbody>\n</table>\n<aside class=\"note note-important\"><p>Use <code translate=\"no\">showable</code> method on pages collection to return only published and not <em>virtual/redirect/excluded</em> pages.</p>\n<p><em>Example:</em></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> page in page.pages.showable %}</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) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ page.title }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span></code></pre></aside>\n<h3 id=\"nested-sections\">Nested sections</h3>\n<p>In a <a href=\"../content/1-pages.md#sub-section\">nested sections</a> context, <code translate=\"no\">page.parent</code>, <code translate=\"no\">page.ancestors</code>, <code translate=\"no\">page.sections</code> and <code translate=\"no\">page.toplevel</code> help you build navigation.</p>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n<th>Example</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">page.parent</code></td>\n<td>Parent <em>section</em>'s page (<code translate=\"no\">null</code> if none).</td>\n<td><em>Page</em></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.ancestors</code></td>\n<td>Collection of ancestor <em>sections</em> (nearest first).</td>\n<td><em>Collection</em></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.sections</code></td>\n<td>Collection of immediate descendant <em>sections</em>.</td>\n<td><em>Collection</em></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.toplevel</code></td>\n<td><code translate=\"no\">true</code> if the page is a top level <em>section</em>.</td>\n<td><em>Boolean</em></td>\n</tr>\n</tbody>\n</table>\n<p><em>Breadcrumb (from the home page to the current page):</em></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> <span class=\"hljs-attr\">aria-label</span>=<span class=\"hljs-string\">\"breadcrumb\"</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul</span>&gt;</span>\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(site.home) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ site.title }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">li</span>&gt;</span>\n    </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">for</span></span> section in page.ancestors|<span class=\"hljs-keyword\">reverse</span> %}</span><span class=\"xml\">\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(section) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ section.title }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">li</span>&gt;</span>\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-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">if</span></span> page.id != site.home %}</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(page) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">aria-current</span>=<span class=\"hljs-string\">\"page\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ page.title }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">li</span>&gt;</span>\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\">ul</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">nav</span>&gt;</span></span></code></pre>\n<aside class=\"note note-tip\"><p>A ready-to-use <a href=\"https://github.com/Cecilapp/Cecil/blob/main/resources/layouts/partials/breadcrumb.html.twig\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">breadcrumb.html.twig</code></a> partial is available:</p>\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/breadcrumb.html.twig')</span> }}</span></code></pre></aside>\n<p><em>Sub-sections menu (immediate descendant sections of the current section):</em></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.sections|<span class=\"hljs-keyword\">length</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\">for</span></span> section in page.sections|sort_by_title %}</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(section) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ section.title }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">li</span>&gt;</span>\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\">endif</span></span> %}</span></code></pre>\n<p><em>Main navigation limited to top level sections (from any page):</em></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><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">for</span></span> section in site.page(site.home).sections|sort_by_title %}</span><span class=\"xml\">\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(section) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ section.title }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\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></span></code></pre>\n<p><em>Link to the parent section:</em></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.<span class=\"hljs-name\">parent</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.<span class=\"hljs-name\">parent</span>) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span>← </span><span class=\"hljs-template-variable\">{{ page.<span class=\"hljs-name\">parent</span>.title }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endif</span></span> %}</span></code></pre>\n<h3 id=\"page-prev-next\">page.&lt;prev/next&gt;</h3>\n<p>Navigation between pages within the same <em>section</em>, sorted according to the section's <code translate=\"no\">sortby</code> (chronological order for dates).</p>\n<p>With <a href=\"../content/1-pages.md#sub-section\">sub-sections</a>, navigation follows the sections tree: the pages of a top level <em>Section</em> and of all its sub-sections are chained, each sub-section (its index page) being placed among the pages of its parent <em>Section</em> and followed by its own pages.</p>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n<th>Example</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">page.prev</code></td>\n<td>Previous page.</td>\n<td><em>Page</em></td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.next</code></td>\n<td>Next page.</td>\n<td><em>Page</em></td>\n</tr>\n</tbody>\n</table>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a</span> <span class=\"hljs-attr\">href</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ url(page.prev) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ page.prev.title }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span></span></code></pre>\n<h3 id=\"page-paginator\">page.paginator</h3>\n<p><em>Paginator</em> helps you build navigation for list pages: homepage, sections, and taxonomies.</p>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">page.paginator.pages</code></td>\n<td>Pages Collection.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.paginator.pages_total</code></td>\n<td>Number total of pages.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.paginator.count</code></td>\n<td>Number of paginator's pages.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.paginator.current</code></td>\n<td>Position index of the current page.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.paginator.links.first</code></td>\n<td>Page ID of the first page.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.paginator.links.prev</code></td>\n<td>Page ID of the previous page.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.paginator.links.self</code></td>\n<td>Page ID of the current page.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.paginator.links.next</code></td>\n<td>Page ID of the next page.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.paginator.links.last</code></td>\n<td>Page ID of the last page.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.paginator.links.path</code></td>\n<td>Page ID without the position index.</td>\n</tr>\n</tbody>\n</table>\n<aside class=\"note note-important\"><p>Because links entries are Page ID you must use the <code translate=\"no\">url()</code> function to create working links.<br>\ne.g: <code translate=\"no\">{{ url(page.paginator.links.next) }}</code></p></aside>\n<p><em>Example:</em></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\">div</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>&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><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>&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\">div</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><em>Example:</em></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\">div</span>&gt;</span>\n  </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">for</span></span> paginator_index in 1..page.paginator.count %}</span><span class=\"xml\">\n    </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">if</span></span> paginator_index != page.paginator.current %}</span><span class=\"xml\">\n      </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">if</span></span> paginator_index == 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\">{{ paginator_index }}</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 ~ '/' ~ paginator_index) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ paginator_index }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\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\">else</span> %}</span><span class=\"xml\">\n  </span><span class=\"hljs-template-variable\">{{ paginator_index }}</span><span class=\"xml\">\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\">div</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<h3 id=\"taxonomy\">Taxonomy</h3>\n<p>Variables available in <em>vocabulary</em> and <em>term</em> templates.</p>\n<h4>Vocabulary</h4>\n<p>Page <code translate=\"no\">/&lt;plural&gt;/</code> (e.g.: <code translate=\"no\">/categories/</code>).</p>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">page.plural</code></td>\n<td>Vocabulary name in plural form.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.singular</code></td>\n<td>Vocabulary name in singular form.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.terms</code></td>\n<td>List of terms (<em>Collection</em>).</td>\n</tr>\n</tbody>\n</table>\n<p>Each term of <code translate=\"no\">page.terms</code> provides <code translate=\"no\">term.id</code> (term ID, e.g.: <code translate=\"no\">categories/php</code>), <code translate=\"no\">term.name</code> (term name, e.g.: <code translate=\"no\">PHP</code>) and the number of its pages with <code translate=\"no\">term|length</code>.</p>\n<h4>Term</h4>\n<p>Page <code translate=\"no\">/&lt;plural&gt;/&lt;term&gt;/</code> (e.g.: <code translate=\"no\">/categories/php/</code>).</p>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">page.title</code></td>\n<td>Term name.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.term</code></td>\n<td>Term ID (e.g.: <code translate=\"no\">categories/php</code>).</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.plural</code></td>\n<td>Vocabulary name in plural form.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.singular</code></td>\n<td>Vocabulary name in singular form.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">page.pages</code></td>\n<td>List of pages in this term, sorted by date (<em>Collection</em>).</td>\n</tr>\n</tbody>\n</table>\n<h4>Taxonomy example</h4>\n<p>Configuration:</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></code></pre>\n<p>Page front matter:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">categories:</span> <span class=\"hljs-string\">[\"Data</span> <span class=\"hljs-string\">Sovereignty\"]</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<p>List of terms (<code translate=\"no\">/categories/</code>), in <code translate=\"no\">layouts/taxonomy/categories.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<p>List of pages of a term (<code translate=\"no\">/categories/data-sovereignty/</code>), in <code translate=\"no\">layouts/taxonomy/category.html.twig</code>:</p>\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<p>Links to the terms of the current page, in a page 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> category in page.categories ?? [] %}</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('categories/' ~ category) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ category }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endfor</span></span> %}</span></code></pre>\n<aside class=\"note note-tip\"><p>The <a href=\"reference/1-functions.md#url\"><code translate=\"no\">url()</code></a> function slugifies the given string to find the matching page: <code translate=\"no\">url('categories/Data Sovereignty')</code> returns <code translate=\"no\">/categories/data-sovereignty/</code>.</p>\n<p>You can also use the built-in partial <code translate=\"no\">{{ include('partials/terms-list.html.twig', {vocabulary: 'categories'}) }}</code>.</p></aside>\n<h2 id=\"cecil\">cecil</h2>\n<table>\n<thead>\n<tr>\n<th>Variable</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">cecil.url</code></td>\n<td>URL of the Cecil website.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">cecil.version</code></td>\n<td>Cecil current version.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">cecil.poweredby</code></td>\n<td>Print <code translate=\"no\">Cecil v%s</code>, with <code translate=\"no\">%s</code> is the current version.</td>\n</tr>\n</tbody>\n</table>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/templates/reference/sorts/",
      "url": "https://cecil-945.pages.dev/documentation/templates/reference/sorts/",
      "title": "Sorts",
      "summary": "Sort collections of pages, menus or taxonomies.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Sorts\nSorting collections (of pages, menus or taxonomies).\nsort_by_title\nSorts a collection by title (with natural sort).\n{{ collection|sort_by_title }}\nExample:\n{{ site.pages|sort_by_title }}\nsort_by_date\nSorts a collection by date (most recent first).\n{{ collection|sort_by_date(variable='date', desc_title=false) }}\nExample:\n{# sort by date #}\n{{ site.pages|sort_by_date }}\n{# sort by updated variable instead of date #}\n{{ site.pages|sort_by_date(variable='updated') }}\n{# sort items with the same date by desc title #}\n{{ site.pages|sort_by_date(desc_title=true) }}\n{# reverse sort #}\n{{ site.pages|sort_by_date|reverse }}\nsort_by_weight\nSorts a collection by weight (lighter first).\n{{ collection|sort_by_weight }}\nExample:\n{{ site.menus.main|sort_by_weight }}\nsort\nFor more complex cases, you should use Twig’s native sort.\nExample:\n{% set files = site.static|sort((a, b) =&gt; a.date|date('U') &lt; b.date|date('U')) %}",
      "content_html": "<h1>Sorts</h1>\n<p>Sorting collections (of pages, menus or taxonomies).</p>\n<h2 id=\"sort-by-title\">sort_by_title</h2>\n<p>Sorts a collection by title (with <a href=\"https://en.wikipedia.org/wiki/Natural_sort_order\" target=\"_blank\" rel=\"noopener noreferrer\">natural sort</a>).</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ collection|sort_by_title }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ site.pages|sort_by_title }}</span></code></pre>\n<h2 id=\"sort-by-date\">sort_by_date</h2>\n<p>Sorts a collection by date (most recent first).</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ collection|sort_by_date(variable='<span class=\"hljs-name\">date</span>', desc_title=false) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# sort by date #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ site.pages|sort_by_date }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# sort by updated variable instead of date #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ site.pages|sort_by_date(variable='updated') }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# sort items with the same date by desc title #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ site.pages|sort_by_date(desc_title=true) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# reverse sort #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ site.pages|sort_by_date|<span class=\"hljs-keyword\">reverse</span> }}</span></code></pre>\n<h2 id=\"sort-by-weight\">sort_by_weight</h2>\n<p>Sorts a collection by weight (lighter first).</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ collection|sort_by_weight }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ site.menus.main|sort_by_weight }}</span></code></pre>\n<h2 id=\"sort\">sort</h2>\n<p>For more complex cases, you should use <a href=\"https://twig.symfony.com/doc/filters/sort.html\" target=\"_blank\" rel=\"noopener noreferrer\">Twig’s native <code translate=\"no\">sort</code></a>.</p>\n<p><em>Example:</em></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> files = site.static|<span class=\"hljs-keyword\">sort</span>((a, b) =&gt; a.<span class=\"hljs-name\">date</span>|<span class=\"hljs-keyword\">date</span>('U') &lt; b.<span class=\"hljs-name\">date</span>|<span class=\"hljs-keyword\">date</span>('U')) %}</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/content/markdown/",
      "url": "https://cecil-945.pages.dev/documentation/content/markdown/",
      "title": "Markdown",
      "summary": "Markdown syntax and extensions: attributes, links, images, table of contents, notes, syntax highlight, etc.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-12T00:00:00+00:00","content_text": "Markdown\nCecil supports Markdown format, but also Markdown Extra.\nCecil also provides extra features to enhance your content, see below.\nAttributes\nWith Markdown Extra you can set an id, class and custom attributes on certain elements using an attribute block.\nFor instance, put the desired attribute(s) after a header, a fenced code block, a link or an image at the end of the line inside curly brackets, like this:\n## Header {#id .class attribute=value}\nFor an inline element, like a link, you must use a line break after the closing brace:\nLorem ipsum [dolor](url){attribute=value} \nsit amet.\nLinks\nYou can create a link with the syntax [Text](url), where url can be a path, a relative path to a Markdown file, an external URL, etc.\nExample:\n[Link to a path](\/about\/)\n[Link to a Markdown file](\/about\/)\n[Link to Cecil website](https:\/\/cecil.app)\nA relative link to a Markdown file is resolved from the folder of the current file (as on GitHub), then replaced by the URL of the targeted page.\nLink to a page\nYou can easily create a link to a page with the syntax [Page title](page:page-id).\nExample:\n[Link to a blog post](page:blog\/post-1)\nExternal\nBy default external links have the following value for rel attribute: noopener noreferrer.\nExample:\n&lt;a href=\"&lt;url&gt;\" rel=\"noopener noreferrer\"&gt;Link to another website&lt;\/a&gt;\nYou can change this behavior with pages.body.links.external options.\nEmbedded links\nCecil can try to turn a link into embedded content by using the {embed} attribute or by setting the global configuration option pages.body.links.embed.enabled to true.\nOnly YouTube, Vimeo, Dailymotion, and GitHub Gists links are supported.\nExample:\n[CECIL : LE générateur de SITES STATIQUES en PHP](https:\/\/www.youtube.com\/watch?v=ur8koU0iYvc){embed}\n\n\n\nLocal video\/audio files\nCecil can also create a video and audio HTML elements, through the file extension.\nExample:\n[Video file](video.mp4){embed controls poster=\/images\/video-test.png}\n[Audio file](song.mp3){embed controls}\nIs converted to:\n&lt;video src=\"\/video.mp4\" controls poster=\"\/images\/video-test.png\" style=\"max-width:100%;height:auto;\"&gt;&lt;\/video&gt;\n&lt;audio src=\"\/song.mp3\" controls&gt;&lt;\/audio&gt;\nWith {embed=false} (or when embedding is disabled), a local video\/audio file stays a link, but its URL points to the published file (fingerprinted if assets.fingerprint is enabled).\nImages\nTo add an image, use an exclamation mark (!) followed by alternative description in brackets ([]), and the path or URL to the image in parentheses (()).\nYou can optionally add a title in quotation marks.\n![Alternative description](\/image.jpg \"Image title\")\nThe path should be relative to the root of your website (e.g.: \/image.jpg), however Cecil is able to normalize a path relative to assets and static directories (e.g.: ..\/..\/assets\/image.jpg).\nLazy loading\nCecil adds the attribute loading=\"lazy\" to each image.\nExample:\n![](\/image.jpg)\nIs converted to:\n&lt;img src=\"\/image.jpg\" loading=\"lazy\"&gt;\nYou can disable this behavior with the attribute {loading=eager} or with the lazy option.\nDecoding\nCecil adds the attribute decoding=\"async\" to each image.\nExample:\n![](\/image.jpg)\nIs converted to:\n&lt;img src=\"\/image.jpg\" decoding=\"async\"&gt;\nYou can disable this behavior with the attribute {decoding=auto} or with the decoding option.\nResize\nEach image in the body can be resized automatically by setting a smaller width than the original one, with the extra attribute {width=X}.\nExample:\n![](\/image.jpg){width=800}\nIs converted to:\n&lt;img src=\"\/thumbnails\/800\/image.jpg\" width=\"800\" height=\"600\"&gt;\nRatio is preserved (height attribute is calculated automatically), the original file is not altered and the resized version is stored in \/thumbnails\/&lt;width&gt;\/.\nThis feature requires an image processing library: Imagick is used first if available (and able to read JPEG and PNG), then libvips (through the PHP FFI extension), and finally GD as fallback; otherwise it only adds a width HTML attribute to the img tag.\nlibvips support is optional and is not bundled with cecil.phar. To use it, Cecil must be installed with Composer, and you need:\n\nlibvips installed on your system\nthe PHP FFI extension enabled\nthe intervention\/image-driver-vips package installed alongside Cecil\n\nIf Cecil is a dependency of your project (see Library):\ncomposer require intervention\/image-driver-vips\nIf Cecil is installed globally:\ncomposer global require cecil\/cecil intervention\/image-driver-vips\nFormats\nIf the formats option is defined, alternatives images are created and added.\nExample:\n![](\/image.jpg)\nCould be converted to:\n&lt;picture&gt;\n  &lt;source srcset=\"\/image.avif\" type=\"image\/avif\"&gt;\n  &lt;source srcset=\"\/image.webp\" type=\"image\/webp\"&gt;\n  &lt;img src=\"\/image.jpg\"&gt;\n&lt;\/picture&gt;\nPlease note that not all image formats are always included in the PHP image extensions.\nResponsive\nIf the responsive option is enabled, then all images in the body will be made responsive automatically.\nExample:\n![](\/image.jpg){width=800}\nwill be converted to:\n&lt;img src=\"\/thumbnails\/800\/image.jpg\" width=\"800\" height=\"600\"\n  srcset=\"\/thumbnails\/320\/image.jpg 320w,\n          \/thumbnails\/640\/image.jpg 640w,\n          \/thumbnails\/800\/image.jpg 800w\"\n  sizes=\"100vw\"\n&gt;\nBecause a body image is converted into an Asset, the different widths must be defined in assets configuration.\nThe sizes attribute takes the value of the assets.images.responsive.sizes.default configuration option, but it can be changed by creating a new entry named after a class added to the image.\nExample:\nassets:\n  images:\n    responsive:\n      sizes:\n        default: 100vw\n        my_class: \"(max-width: 800px) 768px, 1024px\"\n![](\/image.jpg){.my_class}\nYou can combine formats and responsive options.\nCSS class\nYou can set a default value to the class attribute of each image with the class option.\nCaption\nThe optional title can be used to create a caption (figcaption) automatically by enabling the caption option.\nExample:\n![](\/images\/img.jpg \"Title\")\nIs converted to:\n&lt;figure&gt;\n  &lt;img src=\"\/image.jpg\" title=\"Title\"&gt;\n  &lt;figcaption&gt;Title&lt;\/figcaption&gt;\n&lt;\/figure&gt;\nCaption supports Markdown content.\nLocalized image\nFor translated pages, Cecil first looks for a language-suffixed file when resolving Markdown image paths.\nExample:\n![](\/images\/cecil-logo.png)\nWith a French page (fr), Cecil tries \/images\/cecil-logo.fr.png first, then falls back to \/images\/cecil-logo.png.\nPlaceholder\nAs images are typically heavier and slower resources, and they don’t block rendering, we should attempt to give users something to look at while they wait for the image to arrive.\nThe placeholder attribute accepts two options:\n\ncolor: display a colored background (based on image dominant color)\nlqip: Low-Quality Image Placeholder\n\nExamples:\n![](\/images\/img.jpg){placeholder=color}\n![](\/images\/img.jpg){placeholder=lqip}\nYou can set a value to the placeholder attribute for each image with the placeholder option.\nThe lqip option is not compatible with animated GIF.\nTable of contents\nYou can add a table of contents with the following Markdown syntax:\n[toc]\nBy default, the ToC extracts H2 and H3 headings. You can change this behavior with body options.\nExcerpt\nAn excerpt can be defined in the body with one of those following tags: excerpt or break.\nExample:\nIntroduction.\n&lt;!-- excerpt --&gt;\nMain content.\nThen use the excerpt_html filter in your template.\nNotes\nCreate a Note block (info, tips, important, etc.).\nExample:\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;\nTip: This is advice.\nOthers examples:\nempty\ninfo\ntip\nimportant\nwarning\ncaution\nSyntax highlight\nCode block syntax highlighting is enabled by default with the pages.body.highlight option.\nIf needed, you can disable it with:\npages:\n  body:\n    highlight: false\nExample:\n\n```php\necho \"Hello world\";\n```\n\nIs rendered to:\necho \"Hello world\";\nYou can customize the syntax highlighting style by creating your own theme. See the Highlight.js Theme Guide.\nInserted text\nRepresents a range of text that has been added.\n++text++\nIs converted to:\n&lt;ins&gt;text&lt;\/ins&gt;",
      "content_html": "<h1>Markdown</h1>\n<p>Cecil supports <a href=\"http://daringfireball.net/projects/markdown/syntax\" target=\"_blank\" rel=\"noopener noreferrer\">Markdown</a> format, but also <a href=\"https://michelf.ca/projects/php-markdown/extra/\" target=\"_blank\" rel=\"noopener noreferrer\">Markdown Extra</a>.</p>\n<p>Cecil also provides <strong>extra features</strong> to enhance your content, see below.</p>\n<h2 id=\"attributes\">Attributes</h2>\n<p>With <a href=\"https://michelf.ca/projects/php-markdown/extra/\" target=\"_blank\" rel=\"noopener noreferrer\">Markdown Extra</a> you can set an id, class and custom attributes on certain elements using an attribute block.<br>\nFor instance, put the desired attribute(s) after a header, a fenced code block, a link or an image at the end of the line inside curly brackets, like this:</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\"><span class=\"hljs-section\">## Header {#id .class attribute=value}</span></code></pre>\n<aside class=\"note note-warning\"><p>For an inline element, like a link, you must use a line break after the closing brace:</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">Lorem ipsum [<span class=\"hljs-string\">dolor</span>](<span class=\"hljs-link\">url</span>){attribute=value} \nsit amet.</code></pre></aside>\n<h2 id=\"links\">Links</h2>\n<p>You can create a link with the syntax <code translate=\"no\">[Text](url)</code>, where <code translate=\"no\">url</code> can be a path, a relative path to a Markdown file, an external URL, etc.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">[<span class=\"hljs-string\">Link to a path</span>](<span class=\"hljs-link\">/about/</span>)\n[<span class=\"hljs-string\">Link to a Markdown file</span>](<span class=\"hljs-link\">/about/</span>)\n[<span class=\"hljs-string\">Link to Cecil website</span>](<span class=\"hljs-link\">https://cecil.app</span>)</code></pre>\n<aside class=\"note note-info\"><p>A relative link to a Markdown file is resolved from the folder of the current file (as on GitHub), then replaced by the URL of the targeted page.</p></aside>\n<h3 id=\"link-to-a-page\">Link to a page</h3>\n<p>You can easily create a link to a page with the syntax <code translate=\"no\">[Page title](page:page-id)</code>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">[<span class=\"hljs-string\">Link to a blog post</span>](<span class=\"hljs-link\">page:blog/post-1</span>)</code></pre>\n<h3 id=\"external\">External</h3>\n<p>By default external links have the following value for <code translate=\"no\">rel</code> attribute: <code translate=\"no\">noopener noreferrer</code>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-html hljs xml\" translate=\"no\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a</span> <span class=\"hljs-attr\">href</span>=<span class=\"hljs-string\">\"&lt;url&gt;\"</span> <span class=\"hljs-attr\">rel</span>=<span class=\"hljs-string\">\"noopener noreferrer\"</span>&gt;</span>Link to another website<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span></code></pre>\n<p>You can change this behavior with <a href=\"../configuration/4-pages.md#pages-body-links\"><code translate=\"no\">pages.body.links.external</code> options</a>.</p>\n<h3 id=\"embedded-links\">Embedded links</h3>\n<p>Cecil can try to turn a link into embedded content by using the <code translate=\"no\">{embed}</code> attribute or by setting the global configuration option <code translate=\"no\">pages.body.links.embed.enabled</code> to <code translate=\"no\">true</code>.</p>\n<aside class=\"note note-important\"><p>Only <strong>YouTube</strong>, <strong>Vimeo</strong>, <strong>Dailymotion</strong>, and <strong>GitHub Gists</strong> links are supported.</p></aside>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">[<span class=\"hljs-string\">CECIL : LE générateur de SITES STATIQUES en PHP</span>](<span class=\"hljs-link\">https://www.youtube.com/watch?v=ur8koU0iYvc</span>){embed}</code></pre>\n<p><div style=\"position:relative;padding-bottom:56.25%;height:0;overflow:hidden;\">\n<iframe src=\"https://www.youtube-nocookie.com/embed/ur8koU0iYvc\" loading=\"lazy\" width=\"640\" height=\"360\" frameborder=\"0\" allow=\"accelerometer;autoplay;encrypted-media;gyroscope;picture-in-picture;fullscreen;web-share;\" allowfullscreen=\"\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;border:0;background-color:#d8d8d8;\"></iframe>\n</div></p>\n<h4>Local video/audio files</h4>\n<p>Cecil can also create a video and audio HTML elements, through the file extension.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">[<span class=\"hljs-string\">Video file</span>](<span class=\"hljs-link\">video.mp4</span>){embed controls poster=/images/video-test.png}\n[<span class=\"hljs-string\">Audio file</span>](<span class=\"hljs-link\">song.mp3</span>){embed controls}</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\">video</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/video.mp4\"</span> <span class=\"hljs-attr\">controls</span> <span class=\"hljs-attr\">poster</span>=<span class=\"hljs-string\">\"/images/video-test.png\"</span> <span class=\"hljs-attr\">style</span>=<span class=\"hljs-string\">\"max-width:100%;height:auto;\"</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">video</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">audio</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/song.mp3\"</span> <span class=\"hljs-attr\">controls</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">audio</span>&gt;</span></code></pre>\n<p>With <code translate=\"no\">{embed=false}</code> (or when embedding is disabled), a local video/audio file stays a link, but its URL points to the published file (fingerprinted if <a href=\"../configuration/6-assets.md\"><code translate=\"no\">assets.fingerprint</code></a> is enabled).</p>\n<h2 id=\"images\">Images</h2>\n<p>To add an image, use an exclamation mark (<code translate=\"no\">!</code>) followed by alternative description in brackets (<code translate=\"no\">[]</code>), and the path or URL to the image in parentheses (<code translate=\"no\">()</code>).<br>\nYou can optionally add a title in quotation marks.</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![<span class=\"hljs-string\">Alternative description</span>](<span class=\"hljs-link\">/image.jpg \"Image title\"</span>)</code></pre>\n<aside class=\"note note-info\"><p>The path should be relative to the root of your website (e.g.: <code translate=\"no\">/image.jpg</code>), however Cecil is able to normalize a path relative to <em>assets</em> and <em>static</em> directories (e.g.: <code translate=\"no\">../../assets/image.jpg</code>).</p></aside>\n<h3 id=\"lazy-loading\">Lazy loading</h3>\n<p>Cecil adds the attribute <code translate=\"no\">loading=\"lazy\"</code> to each image.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![](/image.jpg)</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\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/image.jpg\"</span> <span class=\"hljs-attr\">loading</span>=<span class=\"hljs-string\">\"lazy\"</span>&gt;</span></code></pre>\n<aside class=\"note note-info\"><p>You can disable this behavior with the attribute <code translate=\"no\">{loading=eager}</code> or with the <a href=\"../configuration/4-pages.md#pages-body-images\"><code translate=\"no\">lazy</code> option</a>.</p></aside>\n<h3 id=\"decoding\">Decoding</h3>\n<p>Cecil adds the attribute <code translate=\"no\">decoding=\"async\"</code> to each image.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![](/image.jpg)</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\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/image.jpg\"</span> <span class=\"hljs-attr\">decoding</span>=<span class=\"hljs-string\">\"async\"</span>&gt;</span></code></pre>\n<aside class=\"note note-info\"><p>You can disable this behavior with the attribute <code translate=\"no\">{decoding=auto}</code> or with the <a href=\"../configuration/4-pages.md#pages-body-images\"><code translate=\"no\">decoding</code> option</a>.</p></aside>\n<h3 id=\"resize\">Resize</h3>\n<p>Each image in the <em>body</em> can be resized automatically by setting a smaller width than the original one, with the extra attribute <code translate=\"no\">{width=X}</code>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![](/image.jpg){width=800}</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\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/thumbnails/800/image.jpg\"</span> <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"800\"</span> <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"600\"</span>&gt;</span></code></pre>\n<aside class=\"note note-info\"><p>Ratio is preserved (<code translate=\"no\">height</code> attribute is calculated automatically), the original file is not altered and the resized version is stored in <code translate=\"no\">/thumbnails/&lt;width&gt;/</code>.</p></aside>\n<aside class=\"note note-important\"><p>This feature requires an image processing library: <a href=\"https://www.php.net/manual/book.imagick.php\" target=\"_blank\" rel=\"noopener noreferrer\">Imagick</a> is used first if available (and able to read JPEG and PNG), then <a href=\"https://www.libvips.org/\" target=\"_blank\" rel=\"noopener noreferrer\">libvips</a> (through the PHP <a href=\"https://www.php.net/manual/book.ffi.php\" target=\"_blank\" rel=\"noopener noreferrer\">FFI</a> extension), and finally <a href=\"https://www.php.net/manual/book.image.php\" target=\"_blank\" rel=\"noopener noreferrer\">GD</a> as fallback; otherwise it only adds a <code translate=\"no\">width</code> HTML attribute to the <code translate=\"no\">img</code> tag.</p></aside>\n<aside class=\"note note-info\"><p>libvips support is optional and is not bundled with <code translate=\"no\">cecil.phar</code>. To use it, Cecil must be installed with <a href=\"https://getcomposer.org\" target=\"_blank\" rel=\"noopener noreferrer\">Composer</a>, and you need:</p>\n<ol>\n<li><a href=\"https://www.libvips.org/install.html\" target=\"_blank\" rel=\"noopener noreferrer\">libvips</a> installed on your system</li>\n<li>the PHP <a href=\"https://www.php.net/manual/book.ffi.php\" target=\"_blank\" rel=\"noopener noreferrer\">FFI</a> extension enabled</li>\n<li>the <code translate=\"no\">intervention/image-driver-vips</code> package installed alongside Cecil</li>\n</ol>\n<p>If Cecil is a dependency of your project (see <a href=\"../developers/2-library.md#libvips-support\">Library</a>):</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">composer require intervention/image-driver-vips</code></pre>\n<p>If Cecil is installed globally:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">composer global require cecil/cecil intervention/image-driver-vips</code></pre></aside>\n<h3 id=\"formats\">Formats</h3>\n<p>If the <a href=\"../configuration/4-pages.md#pages-body-images\"><code translate=\"no\">formats</code> option</a> is defined, alternatives images are created and added.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![](/image.jpg)</code></pre>\n<p>Could be converted to:</p>\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\">srcset</span>=<span class=\"hljs-string\">\"/image.avif\"</span> <span class=\"hljs-attr\">type</span>=<span class=\"hljs-string\">\"image/avif\"</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">source</span> <span class=\"hljs-attr\">srcset</span>=<span class=\"hljs-string\">\"/image.webp\"</span> <span class=\"hljs-attr\">type</span>=<span class=\"hljs-string\">\"image/webp\"</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/image.jpg\"</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">picture</span>&gt;</span></code></pre>\n<aside class=\"note note-important\"><p>Please note that <strong>not all image formats</strong> are always included in the PHP image extensions.</p></aside>\n<h3 id=\"responsive\">Responsive</h3>\n<p>If the <a href=\"../configuration/4-pages.md#pages-body-images\"><code translate=\"no\">responsive</code> option</a> is enabled, then all images in the <em>body</em> will be made responsive automatically.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![](/image.jpg){width=800}</code></pre>\n<p>will be converted to:</p>\n<pre><code class=\"language-html hljs xml\" translate=\"no\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"/thumbnails/800/image.jpg\"</span> <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"800\"</span> <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"600\"</span>\n  <span class=\"hljs-attr\">srcset</span>=<span class=\"hljs-string\">\"/thumbnails/320/image.jpg 320w,\n          /thumbnails/640/image.jpg 640w,\n          /thumbnails/800/image.jpg 800w\"</span>\n  <span class=\"hljs-attr\">sizes</span>=<span class=\"hljs-string\">\"100vw\"</span>\n&gt;</span></code></pre>\n<aside class=\"note note-info\"><p>Because a body image is converted into an <a href=\"../assets/index.md#asset\">Asset</a>, the different widths must be defined in <a href=\"../configuration/6-assets.md\">assets configuration</a>.</p></aside>\n<p>The <code translate=\"no\">sizes</code> attribute takes the value of the <code translate=\"no\">assets.images.responsive.sizes.default</code> configuration option, but it can be changed by creating a new entry named after a <em>class</em> added to the image.</p>\n<p><em>Example:</em></p>\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\">sizes:</span>\n        <span class=\"hljs-attr\">default:</span> <span class=\"hljs-string\">100vw</span>\n        <span class=\"hljs-attr\">my_class:</span> <span class=\"hljs-string\">\"(max-width: 800px) 768px, 1024px\"</span></code></pre>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![](/image.jpg){.my_class}</code></pre>\n<aside class=\"note note-info\"><p>You can combine <code translate=\"no\">formats</code> and <code translate=\"no\">responsive</code> options.</p></aside>\n<h3 id=\"css-class\">CSS class</h3>\n<p>You can set a default value to the <code translate=\"no\">class</code> attribute of each image with the <a href=\"../configuration/4-pages.md#pages-body-images\"><code translate=\"no\">class</code> option</a>.</p>\n<h3 id=\"caption\">Caption</h3>\n<p>The optional title can be used to create a caption (<code translate=\"no\">figcaption</code>) automatically by enabling the <a href=\"../configuration/4-pages.md#pages-body-images\"><code translate=\"no\">caption</code> option</a>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![](/images/img.jpg \"Title\")</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\">figure</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\">\"/image.jpg\"</span> <span class=\"hljs-attr\">title</span>=<span class=\"hljs-string\">\"Title\"</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">figcaption</span>&gt;</span>Title<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">figcaption</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">figure</span>&gt;</span></code></pre>\n<aside class=\"note note-info\"><p>Caption supports Markdown content.</p></aside>\n<h3 id=\"localized-image\">Localized image</h3>\n<p>For translated pages, Cecil first looks for a language-suffixed file when resolving Markdown image paths.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![](/images/cecil-logo.png)</code></pre>\n<p>With a French page (<code translate=\"no\">fr</code>), Cecil tries <code translate=\"no\">/images/cecil-logo.fr.png</code> first, then falls back to <code translate=\"no\">/images/cecil-logo.png</code>.</p>\n<h3 id=\"placeholder\">Placeholder</h3>\n<p>As images are typically heavier and slower resources, and they don’t block rendering, we should attempt to give users something to look at while they wait for the image to arrive.</p>\n<p>The <code translate=\"no\">placeholder</code> attribute accepts two options:</p>\n<ol>\n<li><code translate=\"no\">color</code>: display a colored background (based on image dominant color)</li>\n<li><code translate=\"no\">lqip</code>: <a href=\"https://www.guypo.com/introducing-lqip-low-quality-image-placeholders\" target=\"_blank\" rel=\"noopener noreferrer\">Low-Quality Image Placeholder</a></li>\n</ol>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">![](/images/img.jpg){placeholder=color}\n![](/images/img.jpg){placeholder=lqip}</code></pre>\n<aside class=\"note note-tip\"><p>You can set a value to the <code translate=\"no\">placeholder</code> attribute for each image with the <a href=\"../configuration/4-pages.md#pages-body-images\"><code translate=\"no\">placeholder</code> option</a>.</p></aside>\n<aside class=\"note note-warning\"><p>The <code translate=\"no\">lqip</code> option is not compatible with animated GIF.</p></aside>\n<h2 id=\"table-of-contents\">Table of contents</h2>\n<p>You can add a table of contents with the following Markdown syntax:</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">[toc]</code></pre>\n<aside class=\"note note-info\"><p>By default, the ToC extracts H2 and H3 headings. You can change this behavior with <a href=\"../configuration/4-pages.md#pages-body\">body options</a>.</p></aside>\n<h2 id=\"excerpt\">Excerpt</h2>\n<p>An excerpt can be defined in the <em>body</em> with one of those following tags: <code translate=\"no\">excerpt</code> or <code translate=\"no\">break</code>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-html hljs xml\" translate=\"no\">Introduction.\n<span class=\"hljs-comment\">&lt;!-- excerpt --&gt;</span>\nMain content.</code></pre>\n<p>Then use the <a href=\"../templates/reference/3-filters.md#excerpt-html\"><code translate=\"no\">excerpt_html</code> filter</a> in your template.</p>\n<h2 id=\"notes\">Notes</h2>\n<p>Create a <em>Note</em> block (info, tips, important, etc.).</p>\n<p><em>Example:</em></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<aside class=\"note note-tip\"><p><strong>Tip:</strong> This is advice.</p></aside>\n<p><em>Others examples:</em></p>\n<aside class=\"note\"><p>empty</p></aside>\n<aside class=\"note note-info\"><p>info</p></aside>\n<aside class=\"note note-tip\"><p>tip</p></aside>\n<aside class=\"note note-important\"><p>important</p></aside>\n<aside class=\"note note-warning\"><p>warning</p></aside>\n<aside class=\"note note-caution\"><p>caution</p></aside>\n<h2 id=\"syntax-highlight\">Syntax highlight</h2>\n<p>Code block syntax highlighting is enabled by default with the <a href=\"../configuration/4-pages.md#pages-body-highlight\">pages.body.highlight</a> option.</p>\n<p>If needed, you can disable it with:</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\">highlight:</span> <span class=\"hljs-literal\">false</span></code></pre>\n<p><em>Example:</em></p>\n<pre>\n```php\necho \"Hello world\";\n```\n</pre>\n<p>Is rendered to:</p>\n<pre><code class=\"language-php hljs php\" translate=\"no\"><span class=\"hljs-keyword\">echo</span> <span class=\"hljs-string\">\"Hello world\"</span>;</code></pre>\n<aside class=\"note note-info\"><p>You can customize the syntax highlighting style by creating your own theme. See the <a href=\"https://highlightjs.readthedocs.io/en/latest/theme-guide.html\" target=\"_blank\" rel=\"noopener noreferrer\">Highlight.js Theme Guide</a>.</p></aside>\n<h2 id=\"inserted-text\">Inserted text</h2>\n<p>Represents a range of text that has been added.</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">++text++</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\">ins</span>&gt;</span>text<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">ins</span>&gt;</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/getting-started/directory-structure/",
      "url": "https://cecil-945.pages.dev/documentation/getting-started/directory-structure/",
      "title": "Directory structure",
      "summary": "How source files are organized, how the built website looks like and how files are routed to URLs.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Directory structure\nThis page describes how a Cecil project is organized, what the built website looks like and how files are routed to URLs.\nFile system tree\nProject files organization.\n&lt;mywebsite&gt;\n├─ pages\n|  ├─ blog            &lt;- Section\n|  |  ├─ post-1.md    &lt;- Page in Section\n|  |  └─ post-2.md\n|  ├─ projects\n|  |  └─ project-a.md\n|  └─ about.md        &lt;- Root page\n├─ assets\n|  ├─ styles.scss     &lt;- Asset file\n|  └─ logo.png\n├─ static\n|  └─ file.pdf        &lt;- Static file\n└─ data\n   └─ authors.yml     &lt;- Data collection\nBuilt website tree\nResult of the build.\n&lt;mywebsite&gt;\n└─ _site\n   ├─ index.html               &lt;- Generated home page\n   ├─ blog\/\n   |  ├─ index.html            &lt;- Generated list of posts\n   |  ├─ post-1\/index.html     &lt;- A blog post\n   |  └─ post-2\/index.html\n   ├─ projects\/\n   |  ├─ index.html\n   |  └─ project-a\/index.html\n   ├─ about\/index.html\n   ├─ styles.css\n   ├─ logo.png\n   └─ file.pdf\nBy default each page is generated as slugified-filename\/index.html to get a “beautiful“ URL like https:\/\/mywebsite.tld\/section\/slugified-filename\/.\nTo get an “ugly” URL (like 404.html instead of 404\/), set uglyurl: true in page front matter.\nFile based routing\nMarkdown files in the pages directory enable file based routing. Meaning that adding a pages\/my-projects\/project-a.md for instance will make it available at \/project-a in your browser.\nFile:\n                   pages\/my-projects\/project-a.md\n                        └───── filepath ──────┘\nURL:\n    ┌───── baseurl ─────┬─────── path ────────┐\n     https:\/\/example.com\/my-projects\/project-a\/index.html\n                        └─ section ─┴─ slug ──┘\nTwo kinds of prefixes can alter the URL. See the File prefix section below.",
      "content_html": "<h1>Directory structure</h1>\n<p>This page describes how a Cecil project is organized, what the built website looks like and how files are routed to URLs.</p>\n<h2 id=\"file-system-tree\">File system tree</h2>\n<p>Project files organization.</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">&lt;mywebsite&gt;\n├─ pages\n|  ├─ blog            &lt;- Section\n|  |  ├─ post-1.md    &lt;- Page in Section\n|  |  └─ post-2.md\n|  ├─ projects\n|  |  └─ project-a.md\n|  └─ about.md        &lt;- Root page\n├─ assets\n|  ├─ styles.scss     &lt;- Asset file\n|  └─ logo.png\n├─ static\n|  └─ file.pdf        &lt;- Static file\n└─ data\n   └─ authors.yml     &lt;- Data collection</code></pre>\n<h2 id=\"built-website-tree\">Built website tree</h2>\n<p>Result of the build.</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">&lt;mywebsite&gt;\n└─ _site\n   ├─ index.html               &lt;- Generated home page\n   ├─ blog/\n   |  ├─ index.html            &lt;- Generated list of posts\n   |  ├─ post-1/index.html     &lt;- A blog post\n   |  └─ post-2/index.html\n   ├─ projects/\n   |  ├─ index.html\n   |  └─ project-a/index.html\n   ├─ about/index.html\n   ├─ styles.css\n   ├─ logo.png\n   └─ file.pdf</code></pre>\n<aside class=\"note note-info\"><p>By default each page is generated as <code translate=\"no\">slugified-filename/index.html</code> to get a “beautiful“ URL like <code translate=\"no\">https://mywebsite.tld/section/slugified-filename/</code>.</p>\n<p>To get an “ugly” URL (like <code translate=\"no\">404.html</code> instead of <code translate=\"no\">404/</code>), set <code translate=\"no\">uglyurl: true</code> in page <a href=\"../content/1-pages.md#front-matter\">front matter</a>.</p></aside>\n<h2 id=\"file-based-routing\">File based routing</h2>\n<p>Markdown files in the <code translate=\"no\">pages</code> directory enable file based routing. Meaning that adding a <code translate=\"no\">pages/my-projects/project-a.md</code> for instance will make it available at <code translate=\"no\">/project-a</code> in your browser.</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">File:\n                   pages/my-projects/project-a.md\n                        └───── filepath ──────┘\nURL:\n    ┌───── baseurl ─────┬─────── path ────────┐\n     https://example.com/my-projects/project-a/index.html\n                        └─ section ─┴─ slug ──┘</code></pre>\n<aside class=\"note note-important\"><p>Two kinds of prefixes can alter the URL. See the <a href=\"../content/1-pages.md#file-prefix\">File prefix section</a> below.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/templates/reference/filters/",
      "url": "https://cecil-945.pages.dev/documentation/templates/reference/filters/",
      "title": "Filters",
      "summary": "filter_by, markdown_to_html, toc, slugify, excerpt, highlight, preg_*, etc.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Filters\nVariables can be modified by filters. Filters are separated from the variable by a pipe symbol (|). Multiple filters can be chained. The output of one filter is applied to the next.\n{{ page.title|truncate(25)|capitalize }}\nfilter_by\nFilters a pages collection by variable name\/value.\n{{ collection|filter_by(variable, value) }}\nExample:\n{{ pages|filter_by('section', 'blog') }}\nfilter\nFor more complex cases, you should use Twig’s native filter.\nExample:\n{% pages|filter(p =&gt; p.virtual == false and p.id not in ['page-1', 'page-2']) %}\nmarkdown_to_html\nConverts a Markdown string to HTML.\n{{ markdown|markdown_to_html }}\n{% apply markdown_to_html %}\n{# Markdown here #}\n{% endapply %}\nExamples:\n{% set markdown = '**This is bold text**' %}\n{{ markdown|markdown_to_html }}\n{% apply markdown_to_html %}\n**This is bold text**\n{% endapply %}\ntoc\nExtracts only headings matching the given selectors (h2, h3, etc.), or those defined in config pages.body.toc if not specified.\nThe format parameter defines the output format: html or json.\nThe url parameter is used to build links to headings.\n{{ markdown|toc(format, selectors, url) }}\nExamples:\n{{ page.body|toc }}\n{{ page.body|toc('html') }}\n{{ page.body|toc(selectors=['h2']) }}\n{{ page.body|toc(url=url(page)) }}\njson_decode\nConverts a JSON string to an array.\n{{ json|json_decode }}\nExample:\n{% set json = '{\"foo\": \"bar\"}' %}\n{% set array = json|json_decode %}\n{{ array.foo }}\nyaml_parse\nConverts a YAML string to an array.\n{{ yaml|yaml_parse }}\nExample:\n{% set yaml = 'key: value' %}\n{% set array = yaml|yaml_parse %}\n{{ array.key }}\nslugify\nConverts a string to a slug.\n{{ string|slugify }}\nu\nThe u filter wraps a text in a Unicode object (a Symfony UnicodeString instance) that exposes methods to \"manipulate\" the string.\nExample:\n{{ 'cecil_string with twig'|u.camel.title }}\n\nCecilStringWithTwig\n\nsingular\nThe singular filter transforms a given noun in its plural form into its singular version.\n{{ string|singular(locale)}}\nExample:\n{# English (en) rules are used by default #}\n{{ 'partitions'|singular }}\n\npartition\n\n{{ 'partitions'|singular('fr') }}\n\npartition\n\nplural\nThe plural filter transforms a given noun in its singular form into its plural version.\n{{ string|plural(locale)}}\nExample:\n{# English (en) rules are used by default #}\n{{ 'animal'|plural }}\n\nanimals\n\n{{ 'animal'|plural('fr') }}\n\nanimaux\n\nexcerpt\nTruncates a string and appends suffix.\n{{ string|excerpt(length, suffix) }}\n\n\n\nOption\nDescription\nType\nDefault\n\n\n\n\nlength\nTruncates after this number of characters.\ninteger\n450\n\n\nsuffix\nAppends characters.\nstring\n…\n\n\n\nExamples:\n{{ variable|excerpt }}\n{{ variable|excerpt(250, '...') }}\nexcerpt_html\nReads characters before or after &lt;!-- excerpt --&gt; or &lt;!-- break --&gt; tag.\nSee Content documentation for details.\n{{ string|excerpt_html({separator, capture}) }}\n\n\n\nOption\nDescription\nType\nDefault\n\n\n\n\nseparator\nString to use as separator.\nstring\nexcerpt|break\n\n\ncapture\nPart to capture, before or after the separator.\nstring\nbefore\n\n\n\nExamples:\n{{ variable|excerpt_html }}\n{{ variable|excerpt_html({separator: 'excerpt|break', capture: 'before'}) }}\n{{ variable|excerpt_html({capture: 'after'}) }}\nhighlight\nHighlights a code string with highlight.php.\n{{ code|highlight(language) }}\nExamples:\n{{ '&lt;?php echo $highlighted-&gt;value; ?&gt;'|highlight('php') }}\npreg_split\nSplits a string into an array using a regular expression.\n{{ string|preg_split(pattern, limit) }}\nExample:\n{% set headers = page.content|preg_split('\/&lt;br[^&gt;]*&gt;\/') %}\npreg_match_all\nPerforms a regular expression match and return the group for all matches.\n{{ string|preg_match_all(pattern, group) }}\nExample:\n{% set tags = page.content|preg_match_all('\/&lt;[^&gt;]+&gt;(.*)&lt;\\\/[^&gt;]+&gt;\/') %}\nhex_to_rgb\nConverts a hexadecimal color to RGB.\n{{ color|hex_to_rgb }}",
      "content_html": "<h1>Filters</h1>\n<p>Variables can be modified by <a href=\"https://twig.symfony.com/doc/filters/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">filters</a>. Filters are separated from the variable by a pipe symbol (<code translate=\"no\">|</code>). Multiple filters can be chained. The output of one filter is applied to the next.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ page.title|truncate(25)|<span class=\"hljs-keyword\">capitalize</span> }}</span></code></pre>\n<h2 id=\"filter-by\">filter_by</h2>\n<p>Filters a pages collection by variable name/value.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ collection|filter_by(variable, value) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ pages|filter_by('section', 'blog') }}</span></code></pre>\n<h2 id=\"filter\">filter</h2>\n<p>For more complex cases, you should use <a href=\"https://twig.symfony.com/doc/filters/filter.html\" target=\"_blank\" rel=\"noopener noreferrer\">Twig’s native <code translate=\"no\">filter</code></a>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">pages</span>|<span class=\"hljs-keyword\">filter</span>(p =&gt; p.virtual == false and p.id not in ['page-1', 'page-2']) %}</span></code></pre>\n<h2 id=\"markdown-to-html\">markdown_to_html</h2>\n<p>Converts a Markdown string to HTML.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ markdown|markdown_to_html }}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">apply</span></span> markdown_to_html %}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# Markdown here #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endapply</span></span> %}</span></code></pre>\n<p><em>Examples:</em></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> markdown = '**This is bold text**' %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ markdown|markdown_to_html }}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">apply</span></span> markdown_to_html %}</span><span class=\"xml\">\n**This is bold text**\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endapply</span></span> %}</span></code></pre>\n<h2 id=\"toc\">toc</h2>\n<p>Extracts only headings matching the given <code translate=\"no\">selectors</code> (h2, h3, etc.), or those defined in config <code translate=\"no\">pages.body.toc</code> if not specified.<br>\nThe <code translate=\"no\">format</code> parameter defines the output format: <code translate=\"no\">html</code> or <code translate=\"no\">json</code>.<br>\nThe <code translate=\"no\">url</code> parameter is used to build links to headings.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ markdown|toc(format, selectors, url) }}</span></code></pre>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ page.body|toc }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page.body|toc('html') }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page.body|toc(selectors=['h2']) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page.body|toc(url=url(page)) }}</span></code></pre>\n<h2 id=\"json-decode\">json_decode</h2>\n<p>Converts a JSON string to an array.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ json|json_decode }}</span></code></pre>\n<p><em>Example:</em></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> json = '{\"foo\": \"bar\"}' %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> array = json|json_decode %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ array.foo }}</span></code></pre>\n<h2 id=\"yaml-parse\">yaml_parse</h2>\n<p>Converts a YAML string to an array.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ yaml|yaml_parse }}</span></code></pre>\n<p><em>Example:</em></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> yaml = 'key: value' %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> array = yaml|yaml_parse %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ array.key }}</span></code></pre>\n<h2 id=\"slugify\">slugify</h2>\n<p>Converts a string to a slug.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|slugify }}</span></code></pre>\n<h2 id=\"u\">u</h2>\n<p>The <code translate=\"no\">u</code> filter wraps a text in a Unicode object (a <a href=\"https://symfony.com/doc/current/components/string.html\" target=\"_blank\" rel=\"noopener noreferrer\">Symfony UnicodeString instance</a>) that exposes methods to \"manipulate\" the string.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ 'cecil_string with twig'|u.camel.title }}</span></code></pre>\n<blockquote>\n<p>CecilStringWithTwig</p>\n</blockquote>\n<h2 id=\"singular\">singular</h2>\n<p>The <code translate=\"no\">singular</code> filter transforms a given noun in its plural form into its singular version.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|singular(locale)}}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# English (en) rules are used by default #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ 'partitions'|singular }}</span></code></pre>\n<blockquote>\n<p>partition</p>\n</blockquote>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ 'partitions'|singular('fr') }}</span></code></pre>\n<blockquote>\n<p>partition</p>\n</blockquote>\n<h2 id=\"plural\">plural</h2>\n<p>The <code translate=\"no\">plural</code> filter transforms a given noun in its singular form into its plural version.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|plural(locale)}}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# English (en) rules are used by default #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ 'animal'|plural }}</span></code></pre>\n<blockquote>\n<p>animals</p>\n</blockquote>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ 'animal'|plural('fr') }}</span></code></pre>\n<blockquote>\n<p>animaux</p>\n</blockquote>\n<h2 id=\"excerpt\">excerpt</h2>\n<p>Truncates a string and appends suffix.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|excerpt(length, suffix) }}</span></code></pre>\n<table>\n<thead>\n<tr>\n<th>Option</th>\n<th>Description</th>\n<th>Type</th>\n<th>Default</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>length</td>\n<td>Truncates after this number of characters.</td>\n<td>integer</td>\n<td>450</td>\n</tr>\n<tr>\n<td>suffix</td>\n<td>Appends characters.</td>\n<td>string</td>\n<td><code translate=\"no\">…</code></td>\n</tr>\n</tbody>\n</table>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ variable|excerpt }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ variable|excerpt(250, '...') }}</span></code></pre>\n<h2 id=\"excerpt-html\">excerpt_html</h2>\n<p>Reads characters before or after <code translate=\"no\">&lt;!-- excerpt --&gt;</code> or <code translate=\"no\">&lt;!-- break --&gt;</code> tag.<br>\nSee <a href=\"../../content/3-markdown.md#excerpt\">Content documentation</a> for details.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|excerpt_html({separator, capture}) }}</span></code></pre>\n<table>\n<thead>\n<tr>\n<th>Option</th>\n<th>Description</th>\n<th>Type</th>\n<th>Default</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>separator</td>\n<td>String to use as separator.</td>\n<td>string</td>\n<td><code translate=\"no\">excerpt|break</code></td>\n</tr>\n<tr>\n<td>capture</td>\n<td>Part to capture, <code translate=\"no\">before</code> or <code translate=\"no\">after</code> the separator.</td>\n<td>string</td>\n<td><code translate=\"no\">before</code></td>\n</tr>\n</tbody>\n</table>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ variable|excerpt_html }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ variable|excerpt_html({separator: 'excerpt|break', capture: 'before'}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ variable|excerpt_html({capture: 'after'}) }}</span></code></pre>\n<h2 id=\"highlight\">highlight</h2>\n<p>Highlights a code string with <a href=\"https://github.com/scrivo/highlight.php\" target=\"_blank\" rel=\"noopener noreferrer\">highlight.php</a>.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ code|highlight(language) }}</span></code></pre>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ '&lt;?php echo $highlighted-&gt;value; ?&gt;'|highlight('php') }}</span></code></pre>\n<h2 id=\"preg-split\">preg_split</h2>\n<p>Splits a string into an array using a regular expression.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|preg_split(pattern, limit) }}</span></code></pre>\n<p><em>Example:</em></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> headers = page.content|preg_split('/&lt;br[^&gt;]*&gt;/') %}</span></code></pre>\n<h2 id=\"preg-match-all\">preg_match_all</h2>\n<p>Performs a regular expression match and return the group for all matches.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|preg_match_all(pattern, group) }}</span></code></pre>\n<p><em>Example:</em></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> tags = page.content|preg_match_all('/&lt;[^&gt;]+&gt;(.*)&lt;\\/[^&gt;]+&gt;/') %}</span></code></pre>\n<h2 id=\"hex-to-rgb\">hex_to_rgb</h2>\n<p>Converts a hexadecimal color to RGB.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ color|hex_to_rgb }}</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/pages/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/pages/",
      "title": "Pages",
      "summary": "Pages directory, sorting, pagination, paths, body, virtual pages, generators, etc.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Pages\nThese options control how pages are loaded, sorted, paginated, routed and generated.\npages.dir\nDirectory source of pages (pages by default).\npages:\n  dir: pages\npages.ext\nExtensions of pages files.\npages:\n  ext: [md, markdown, mdown, mkdn, mkd, text, txt]\npages.exclude\nDirectories, paths and files name to exclude (accepts globs, strings and regexes).\npages:\n  exclude: ['vendor', 'node_modules', '*.scss', '\/\\.bck$\/']\npages.prefix.separator\nList of characters used as separator between a filename prefix (date or weight) and the slug.\npages:\n  prefix:\n    separator: ['-', '_']\npages.sortby\nDefault collections sort method.\npages:\n  sortby: date # `date`, `updated`, `title` or `weight`\n  # or\n  sortby:\n    variable: date    # `date`, `updated`, `title` or `weight`\n    desc_title: false # sort by title in descending order\n    reverse: false    # reverse the sort order\npages.pagination\nPagination is available for list pages (type is homepage, section or term).\npages:\n  pagination:\n    max: 5     # maximum number of entries per page\n    path: page # path to the paginated page\nDisable pagination\nPagination can be disabled:\npages:\n  pagination: false\npages.paths\nApply a custom path for all pages of a Section.\npages:\n  paths:\n    - section: &lt;section’s ID&gt;\n      path: &lt;path of pages&gt;\nPath placeholders\n\n:year\n:month\n:day\n:section\n:slug\n\nExample:\npages:\n  paths:\n    - section: Blog\n      path: :section\/:year\/:month\/:day\/:slug # e.g.: \/blog\/2020\/12\/01\/my-post\/\n# localized\nlanguages:\n  - code: fr\n    name: Français\n    locale: fr_FR\n    config:\n      pages:\n        paths:\n          - section: Blog\n            path: blogue\/:year\/:month\/:day\/:slug # e.g.: \/blogue\/2020\/12\/01\/mon-billet\/\npages.frontmatter\nPage front matter format (yaml by default, also accepts ini, toml and json).\npages:\n  frontmatter: yaml\npages.body\nPage body options.\nTo know how those options impacts your content see Content &gt; Markdown documentation.\npages.body.toc\nHeaders used to build the table of contents ([h2, h3] by default).\npages:\n  body:\n    toc: [h2, h3]\npages.body.highlight\nEnables code syntax highlighting (true by default).\npages:\n  body:\n    highlight: false # set to false to disable syntax highlighting\npages.body.images\nImages handling options.\npages:\n  body:\n    images:\n      formats: []       # adds alternative image formats as `source` (e.g. `[avif, webp]`, empty array by default)\n      resize: 0         # resizes all images to &lt;width&gt; (in pixels, `0` to disable)\n      responsive: false # adds responsive image variants to the `srcset` attribute (`false` by default)\n      lazy: true        # adds `loading=\"lazy\"` attribute (`true` by default)\n      decoding: true    # adds `decoding=\"async\"` attribute (`true` by default)\n      caption: false    # puts the image in a &lt;figure&gt; element and adds a &lt;figcaption&gt; containing the title (`false` by default)\n      placeholder: ''   # fills the &lt;img&gt; background before loading ('color' or 'lqip', empty by default)\n      class: ''         # sets a default class on each image (empty by default)\n      dark_suffix: ''   # suffix of the dark variant image (e.g. `.dark`), disabled by default\n      mobile_suffix: '' # suffix of the mobile variant image (e.g. `.mobile`), disabled by default\n      mobile_media_query: '(max-width: 767px)' # media query of the mobile variant `&lt;source&gt;`\n      remote:           # remote image handling (set to `false` to disable)\n        fallback:         # path to the fallback image, stored in assets dir (empty by default)\nSince version 8.41.0, the pages.body.images.resize option is used to resize images to a specific width, no more to enable the resize feature (enabled systematically).\nGlobal options, like responsives images widths and sizes, are configurable in the assets.images section.\nRemote images are downloaded and converted into Assets to be manipulated. You can disable this behavior by setting the option pages.body.images.remote.enabled to false.\nWhen dark_suffix is set (e.g. dark_suffix: .dark), Cecil automatically looks for a dark variant of each image (e.g. photo.dark.jpg alongside photo.jpg). If found, the image is wrapped in a &lt;picture&gt; element with a &lt;source media=\"(prefers-color-scheme: dark)\"&gt; for automatic light\/dark theme switching. Works in combination with formats and responsive.\nIn the same way, when mobile_suffix is set (e.g. mobile_suffix: .mobile), Cecil looks for a mobile variant of each image (e.g. photo.mobile.jpg alongside photo.jpg) and adds a &lt;source&gt; element with the mobile_media_query media query ((max-width: 767px) by default). If dark_suffix is also set, the dark variant of the mobile image (e.g. photo.mobile.dark.jpg) is used on mobile with dark color scheme. Mobile sources are placed before dark sources, so that a mobile variant takes precedence.\npages.body.links\nLinks handling options.\npages:\n  body:\n    links:\n      embed:\n        enabled: false     # turns links in embedded content if possible (`false` by default)\n        video: [mp4, webm] # video files extensions\n        audio: [mp3]       # audio files extensions\n      external:\n        blank: false     # if true open external link in new tab\n        noopener: true   # if true add \"noopener\" to `rel` attribute\n        noreferrer: true # if true add \"noreferrer\" to `rel` attribute\n        nofollow: false  # if true add \"nofollow\" to `rel` attribute\npages.body.excerpt\nExcerpt handling options.\npages:\n  body:\n    excerpt:\n      separator: excerpt|break # string to use as separator (`excerpt|break` by default)\n      capture: before          # part to capture, `before` or `after` the separator (`before` by default)\npages.virtual\nVirtual pages is the best way to create pages without content (front matter only).\nIt consists of a list of pages with a path and some front matter variables.\nExample:\npages:\n  virtual:\n    - path: code\n      redirect: https:\/\/github.com\/ArnaudLigny\npages.default\nDefault pages are pages created automatically by Cecil (from built-in templates):\npages:\n  default:\n    index:\n      path: ''\n      title: Home\n      published: true\n    404:\n      path: 404\n      title: Page not found\n      layout: 404\n      uglyurl: true\n      published: true\n      excluded: true\n    robots:\n      path: robots\n      title: Robots.txt\n      layout: robots\n      output: txt\n      published: true\n      excluded: true\n      multilingual: false\n    sitemap:\n      path: sitemap\n      title: XML sitemap\n      layout: sitemap\n      output: xml\n      changefreq: monthly\n      priority: 0.5\n      published: true\n      excluded: true\n      multilingual: false\n    xsl\/atom:\n      path: xsl\/atom\n      layout: feed\n      output: xsl\n      uglyurl: true\n      published: true\n      excluded: true\n    xsl\/rss:\n      path: xsl\/rss\n      layout: feed\n      output: xsl\n      uglyurl: true\n      published: false\n      excluded: true\nThe structure is almost identical of pages.virtual, except the named key.\nEach one can be:\n\ndisabled: published: false\nexcluded from list pages: excluded: true\nexcluded from localization: multilingual: false\n\nSince version 8.68.0 you can override the default robots.txt page by creating a page with the same path:\npages\/robots.md\n---\nlayout: robots\noutput: txt\n---\nUser-agent: AI-bot\nDisallow: \/\npages.generators\nGenerators are used by Cecil to create additional pages (e.g.: sitemap, feed, pagination, etc.) from existing pages, or from other sources like the configuration file or external sources.\nBelow the list of Generators provided by Cecil, in a defined order:\npages:\n  generators:\n    10: 'Cecil\\Generator\\DefaultPages'\n    20: 'Cecil\\Generator\\VirtualPages'\n    30: 'Cecil\\Generator\\ExternalBody'\n    40: 'Cecil\\Generator\\Section'\n    50: 'Cecil\\Generator\\Taxonomy'\n    60: 'Cecil\\Generator\\Homepage'\n    70: 'Cecil\\Generator\\Pagination'\n    80: 'Cecil\\Generator\\Alias'\n    90: 'Cecil\\Generator\\Redirect'\nYou can extend Cecil with Pages generator.\npages.subsets\nSubsets are used to render a part of the pages collection, based on a specific path, language or output format, with the command:\ncecil build --render-subset=&lt;name&gt;\npages:\n  subsets:\n    &lt;name&gt;:\n      path: &lt;path&gt; # glob or string path (e.g.: `blog\/*`, `blog`)\n      language: &lt;language&gt; # language code (e.g.: `en`, `fr`)\n      output: &lt;output&gt; # output format (e.g.: `html`, `atom`)\nExample:\npages:\n  subsets:\n    blog_en:\n      path: blog\n      language: en\n      output: html\n    search_index:\n      path: '*'\n      output: json\n",
      "content_html": "<h1>Pages</h1>\n<p>These options control how pages are loaded, sorted, paginated, routed and generated.</p>\n<h2 id=\"pages-dir\">pages.dir</h2>\n<p>Directory source of pages (<code translate=\"no\">pages</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">pages</span></code></pre>\n<h2 id=\"pages-ext\">pages.ext</h2>\n<p>Extensions of pages files.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">ext:</span> <span class=\"hljs-string\">[md,</span> <span class=\"hljs-string\">markdown,</span> <span class=\"hljs-string\">mdown,</span> <span class=\"hljs-string\">mkdn,</span> <span class=\"hljs-string\">mkd,</span> <span class=\"hljs-string\">text,</span> <span class=\"hljs-string\">txt]</span></code></pre>\n<h2 id=\"pages-exclude\">pages.exclude</h2>\n<p>Directories, paths and files name to exclude (accepts globs, strings and regexes).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">exclude:</span> <span class=\"hljs-string\">['vendor',</span> <span class=\"hljs-string\">'node_modules'</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">'*.scss'</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">'/\\.bck$/'</span><span class=\"hljs-string\">]</span></code></pre>\n<h2 id=\"pages-prefix-separator\">pages.prefix.separator</h2>\n<p>List of characters used as separator between a filename prefix (<code translate=\"no\">date</code> or <code translate=\"no\">weight</code>) and the slug.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">prefix:</span>\n    <span class=\"hljs-attr\">separator:</span> <span class=\"hljs-string\">['-',</span> <span class=\"hljs-string\">'_'</span><span class=\"hljs-string\">]</span></code></pre>\n<h2 id=\"pages-sortby\">pages.sortby</h2>\n<p>Default collections sort method.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">sortby:</span> <span class=\"hljs-string\">date</span> <span class=\"hljs-comment\"># `date`, `updated`, `title` or `weight`</span>\n  <span class=\"hljs-comment\"># or</span>\n  <span class=\"hljs-attr\">sortby:</span>\n    <span class=\"hljs-attr\">variable:</span> <span class=\"hljs-string\">date</span>    <span class=\"hljs-comment\"># `date`, `updated`, `title` or `weight`</span>\n    <span class=\"hljs-attr\">desc_title:</span> <span class=\"hljs-literal\">false</span> <span class=\"hljs-comment\"># sort by title in descending order</span>\n    <span class=\"hljs-attr\">reverse:</span> <span class=\"hljs-literal\">false</span>    <span class=\"hljs-comment\"># reverse the sort order</span></code></pre>\n<h2 id=\"pages-pagination\">pages.pagination</h2>\n<p>Pagination is available for list pages (<em>type</em> is <code translate=\"no\">homepage</code>, <code translate=\"no\">section</code> or <code translate=\"no\">term</code>).</p>\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\">5</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<h3 id=\"disable-pagination\">Disable pagination</h3>\n<p>Pagination can be disabled:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">pagination:</span> <span class=\"hljs-literal\">false</span></code></pre>\n<h2 id=\"pages-paths\">pages.paths</h2>\n<p>Apply a custom <a href=\"../content/2-front-matter.md#predefined-variables\"><code translate=\"no\">path</code></a> for all pages of a <strong><em>Section</em></strong>.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">paths:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">section:</span> <span class=\"hljs-string\">&lt;section’s</span> <span class=\"hljs-string\">ID&gt;</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">&lt;path</span> <span class=\"hljs-string\">of</span> <span class=\"hljs-string\">pages&gt;</span></code></pre>\n<h3 id=\"path-placeholders\">Path placeholders</h3>\n<ul>\n<li><code translate=\"no\">:year</code></li>\n<li><code translate=\"no\">:month</code></li>\n<li><code translate=\"no\">:day</code></li>\n<li><code translate=\"no\">:section</code></li>\n<li><code translate=\"no\">:slug</code></li>\n</ul>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">paths:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">section:</span> <span class=\"hljs-string\">Blog</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">:section/:year/:month/:day/:slug</span> <span class=\"hljs-comment\"># e.g.: /blog/2020/12/01/my-post/</span>\n<span class=\"hljs-comment\"># localized</span>\n<span class=\"hljs-attr\">languages:</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\">pages:</span>\n        <span class=\"hljs-attr\">paths:</span>\n          <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">section:</span> <span class=\"hljs-string\">Blog</span>\n            <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">blogue/:year/:month/:day/:slug</span> <span class=\"hljs-comment\"># e.g.: /blogue/2020/12/01/mon-billet/</span></code></pre>\n<h2 id=\"pages-frontmatter\">pages.frontmatter</h2>\n<p>Page front matter format (<code translate=\"no\">yaml</code> by default, also accepts <code translate=\"no\">ini</code>, <code translate=\"no\">toml</code> and <code translate=\"no\">json</code>).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">frontmatter:</span> <span class=\"hljs-string\">yaml</span></code></pre>\n<h2 id=\"pages-body\">pages.body</h2>\n<p>Page body options.</p>\n<aside class=\"note note-info\"><p>To know how those options impacts your content see <em><a href=\"../content/3-markdown.md\">Content &gt; Markdown</a></em> documentation.</p></aside>\n<h3 id=\"pages-body-toc\">pages.body.toc</h3>\n<p>Headers used to build the table of contents (<code translate=\"no\">[h2, h3]</code> by default).</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></code></pre>\n<h3 id=\"pages-body-highlight\">pages.body.highlight</h3>\n<p>Enables code syntax highlighting (<code translate=\"no\">true</code> by default).</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\">highlight:</span> <span class=\"hljs-literal\">false</span> <span class=\"hljs-comment\"># set to false to disable syntax highlighting</span></code></pre>\n<h3 id=\"pages-body-images\">pages.body.images</h3>\n<p>Images handling options.</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\">images:</span>\n      <span class=\"hljs-attr\">formats:</span> <span class=\"hljs-string\">[]</span>       <span class=\"hljs-comment\"># adds alternative image formats as `source` (e.g. `[avif, webp]`, empty array by default)</span>\n      <span class=\"hljs-attr\">resize:</span> <span class=\"hljs-number\">0</span>         <span class=\"hljs-comment\"># resizes all images to &lt;width&gt; (in pixels, `0` to disable)</span>\n      <span class=\"hljs-attr\">responsive:</span> <span class=\"hljs-literal\">false</span> <span class=\"hljs-comment\"># adds responsive image variants to the `srcset` attribute (`false` by default)</span>\n      <span class=\"hljs-attr\">lazy:</span> <span class=\"hljs-literal\">true</span>        <span class=\"hljs-comment\"># adds `loading=\"lazy\"` attribute (`true` by default)</span>\n      <span class=\"hljs-attr\">decoding:</span> <span class=\"hljs-literal\">true</span>    <span class=\"hljs-comment\"># adds `decoding=\"async\"` attribute (`true` by default)</span>\n      <span class=\"hljs-attr\">caption:</span> <span class=\"hljs-literal\">false</span>    <span class=\"hljs-comment\"># puts the image in a &lt;figure&gt; element and adds a &lt;figcaption&gt; containing the title (`false` by default)</span>\n      <span class=\"hljs-attr\">placeholder:</span> <span class=\"hljs-string\">''</span>   <span class=\"hljs-comment\"># fills the &lt;img&gt; background before loading ('color' or 'lqip', empty by default)</span>\n      <span class=\"hljs-attr\">class:</span> <span class=\"hljs-string\">''</span>         <span class=\"hljs-comment\"># sets a default class on each image (empty by default)</span>\n      <span class=\"hljs-attr\">dark_suffix:</span> <span class=\"hljs-string\">''</span>   <span class=\"hljs-comment\"># suffix of the dark variant image (e.g. `.dark`), disabled by default</span>\n      <span class=\"hljs-attr\">mobile_suffix:</span> <span class=\"hljs-string\">''</span> <span class=\"hljs-comment\"># suffix of the mobile variant image (e.g. `.mobile`), disabled by default</span>\n      <span class=\"hljs-attr\">mobile_media_query:</span> <span class=\"hljs-string\">'(max-width: 767px)'</span> <span class=\"hljs-comment\"># media query of the mobile variant `&lt;source&gt;`</span>\n      <span class=\"hljs-attr\">remote:</span>           <span class=\"hljs-comment\"># remote image handling (set to `false` to disable)</span>\n        <span class=\"hljs-attr\">fallback:</span>         <span class=\"hljs-comment\"># path to the fallback image, stored in assets dir (empty by default)</span></code></pre>\n<aside class=\"note note-warning\"><p>Since version <ins>8.41.0</ins>, the <code translate=\"no\">pages.body.images.resize</code> option is used to resize images to a specific width, no more to enable the resize feature (enabled systematically).</p></aside>\n<aside class=\"note note-important\"><p>Global options, like responsives images widths and sizes, are configurable in the <a href=\"6-assets.md#assets-images\"><code translate=\"no\">assets.images</code></a> section.</p></aside>\n<aside class=\"note note-info\"><p>Remote images are downloaded and converted into <em>Assets</em> to be manipulated. You can disable this behavior by setting the option <code translate=\"no\">pages.body.images.remote.enabled</code> to <code translate=\"no\">false</code>.</p></aside>\n<aside class=\"note note-tip\"><p>When <code translate=\"no\">dark_suffix</code> is set (e.g. <code translate=\"no\">dark_suffix: .dark</code>), Cecil automatically looks for a dark variant of each image (e.g. <code translate=\"no\">photo.dark.jpg</code> alongside <code translate=\"no\">photo.jpg</code>). If found, the image is wrapped in a <code translate=\"no\">&lt;picture&gt;</code> element with a <code translate=\"no\">&lt;source media=\"(prefers-color-scheme: dark)\"&gt;</code> for automatic light/dark theme switching. Works in combination with <code translate=\"no\">formats</code> and <code translate=\"no\">responsive</code>.</p>\n<p>In the same way, when <code translate=\"no\">mobile_suffix</code> is set (e.g. <code translate=\"no\">mobile_suffix: .mobile</code>), Cecil looks for a mobile variant of each image (e.g. <code translate=\"no\">photo.mobile.jpg</code> alongside <code translate=\"no\">photo.jpg</code>) and adds a <code translate=\"no\">&lt;source&gt;</code> element with the <code translate=\"no\">mobile_media_query</code> media query (<code translate=\"no\">(max-width: 767px)</code> by default). If <code translate=\"no\">dark_suffix</code> is also set, the dark variant of the mobile image (e.g. <code translate=\"no\">photo.mobile.dark.jpg</code>) is used on mobile with dark color scheme. Mobile sources are placed before dark sources, so that a mobile variant takes precedence.</p></aside>\n<h3 id=\"pages-body-links\">pages.body.links</h3>\n<p>Links handling options.</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\">links:</span>\n      <span class=\"hljs-attr\">embed:</span>\n        <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">false</span>     <span class=\"hljs-comment\"># turns links in embedded content if possible (`false` by default)</span>\n        <span class=\"hljs-attr\">video:</span> <span class=\"hljs-string\">[mp4,</span> <span class=\"hljs-string\">webm]</span> <span class=\"hljs-comment\"># video files extensions</span>\n        <span class=\"hljs-attr\">audio:</span> <span class=\"hljs-string\">[mp3]</span>       <span class=\"hljs-comment\"># audio files extensions</span>\n      <span class=\"hljs-attr\">external:</span>\n        <span class=\"hljs-attr\">blank:</span> <span class=\"hljs-literal\">false</span>     <span class=\"hljs-comment\"># if true open external link in new tab</span>\n        <span class=\"hljs-attr\">noopener:</span> <span class=\"hljs-literal\">true</span>   <span class=\"hljs-comment\"># if true add \"noopener\" to `rel` attribute</span>\n        <span class=\"hljs-attr\">noreferrer:</span> <span class=\"hljs-literal\">true</span> <span class=\"hljs-comment\"># if true add \"noreferrer\" to `rel` attribute</span>\n        <span class=\"hljs-attr\">nofollow:</span> <span class=\"hljs-literal\">false</span>  <span class=\"hljs-comment\"># if true add \"nofollow\" to `rel` attribute</span></code></pre>\n<h3 id=\"pages-body-excerpt\">pages.body.excerpt</h3>\n<p>Excerpt handling options.</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\">excerpt:</span>\n      <span class=\"hljs-attr\">separator:</span> <span class=\"hljs-string\">excerpt|break</span> <span class=\"hljs-comment\"># string to use as separator (`excerpt|break` by default)</span>\n      <span class=\"hljs-attr\">capture:</span> <span class=\"hljs-string\">before</span>          <span class=\"hljs-comment\"># part to capture, `before` or `after` the separator (`before` by default)</span></code></pre>\n<h2 id=\"pages-virtual\">pages.virtual</h2>\n<p>Virtual pages is the best way to create pages without content (<strong>front matter only</strong>).</p>\n<p>It consists of a list of pages with a <code translate=\"no\">path</code> and some front matter variables.</p>\n<p><em>Example:</em></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/ArnaudLigny</span></code></pre>\n<h2 id=\"pages-default\">pages.default</h2>\n<p>Default pages are pages created automatically by Cecil (from built-in templates):</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\">index:</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">''</span>\n      <span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">Home</span>\n      <span class=\"hljs-attr\">published:</span> <span class=\"hljs-literal\">true</span>\n    <span class=\"hljs-attr\">404:</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-number\">404</span>\n      <span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">Page</span> <span class=\"hljs-string\">not</span> <span class=\"hljs-string\">found</span>\n      <span class=\"hljs-attr\">layout:</span> <span class=\"hljs-number\">404</span>\n      <span class=\"hljs-attr\">uglyurl:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">published:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">excluded:</span> <span class=\"hljs-literal\">true</span>\n    <span class=\"hljs-attr\">robots:</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">robots</span>\n      <span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">Robots.txt</span>\n      <span class=\"hljs-attr\">layout:</span> <span class=\"hljs-string\">robots</span>\n      <span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">txt</span>\n      <span class=\"hljs-attr\">published:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">excluded:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">multilingual:</span> <span class=\"hljs-literal\">false</span>\n    <span class=\"hljs-attr\">sitemap:</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">sitemap</span>\n      <span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">XML</span> <span class=\"hljs-string\">sitemap</span>\n      <span class=\"hljs-attr\">layout:</span> <span class=\"hljs-string\">sitemap</span>\n      <span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">xml</span>\n      <span class=\"hljs-attr\">changefreq:</span> <span class=\"hljs-string\">monthly</span>\n      <span class=\"hljs-attr\">priority:</span> <span class=\"hljs-number\">0.5</span>\n      <span class=\"hljs-attr\">published:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">excluded:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">multilingual:</span> <span class=\"hljs-literal\">false</span>\n    <span class=\"hljs-attr\">xsl/atom:</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">xsl/atom</span>\n      <span class=\"hljs-attr\">layout:</span> <span class=\"hljs-string\">feed</span>\n      <span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">xsl</span>\n      <span class=\"hljs-attr\">uglyurl:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">published:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">excluded:</span> <span class=\"hljs-literal\">true</span>\n    <span class=\"hljs-attr\">xsl/rss:</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">xsl/rss</span>\n      <span class=\"hljs-attr\">layout:</span> <span class=\"hljs-string\">feed</span>\n      <span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">xsl</span>\n      <span class=\"hljs-attr\">uglyurl:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">published:</span> <span class=\"hljs-literal\">false</span>\n      <span class=\"hljs-attr\">excluded:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<aside class=\"note note-info\"><p>The structure is almost identical of <a href=\"#pages-virtual\"><code translate=\"no\">pages.virtual</code></a>, except the named key.</p></aside>\n<p>Each one can be:</p>\n<ol>\n<li>disabled: <code translate=\"no\">published: false</code></li>\n<li>excluded from list pages: <code translate=\"no\">excluded: true</code></li>\n<li>excluded from localization: <code translate=\"no\">multilingual: false</code></li>\n</ol>\n<aside class=\"note note-tip\"><p>Since version 8.68.0 you can override the default <code translate=\"no\">robots.txt</code> page by creating a page with the same <code translate=\"no\">path</code>:</p>\n<p><em>pages/robots.md</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">layout:</span> <span class=\"hljs-string\">robots</span>\n<span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">txt</span>\n<span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">User-agent:</span> <span class=\"hljs-string\">AI-bot</span>\n<span class=\"hljs-attr\">Disallow:</span> <span class=\"hljs-string\">/</span></code></pre></aside>\n<h2 id=\"pages-generators\">pages.generators</h2>\n<p>Generators are used by Cecil to create additional pages (e.g.: sitemap, feed, pagination, etc.) from existing pages, or from other sources like the configuration file or external sources.</p>\n<p>Below the list of Generators provided by Cecil, in a defined order:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">generators:</span>\n    <span class=\"hljs-attr\">10:</span> <span class=\"hljs-string\">'Cecil\\Generator\\DefaultPages'</span>\n    <span class=\"hljs-attr\">20:</span> <span class=\"hljs-string\">'Cecil\\Generator\\VirtualPages'</span>\n    <span class=\"hljs-attr\">30:</span> <span class=\"hljs-string\">'Cecil\\Generator\\ExternalBody'</span>\n    <span class=\"hljs-attr\">40:</span> <span class=\"hljs-string\">'Cecil\\Generator\\Section'</span>\n    <span class=\"hljs-attr\">50:</span> <span class=\"hljs-string\">'Cecil\\Generator\\Taxonomy'</span>\n    <span class=\"hljs-attr\">60:</span> <span class=\"hljs-string\">'Cecil\\Generator\\Homepage'</span>\n    <span class=\"hljs-attr\">70:</span> <span class=\"hljs-string\">'Cecil\\Generator\\Pagination'</span>\n    <span class=\"hljs-attr\">80:</span> <span class=\"hljs-string\">'Cecil\\Generator\\Alias'</span>\n    <span class=\"hljs-attr\">90:</span> <span class=\"hljs-string\">'Cecil\\Generator\\Redirect'</span></code></pre>\n<aside class=\"note note-tip\"><p>You can extend Cecil with <a href=\"../developers/1-extend.md#pages-generator\">Pages generator</a>.</p></aside>\n<h2 id=\"pages-subsets\">pages.subsets</h2>\n<p>Subsets are used to render a part of the pages collection, based on a specific path, language or output format, with the command:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">cecil build --render-subset=&lt;name&gt;</code></pre>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">subsets:</span>\n    <span class=\"hljs-string\">&lt;name&gt;:</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">&lt;path&gt;</span> <span class=\"hljs-comment\"># glob or string path (e.g.: `blog/*`, `blog`)</span>\n      <span class=\"hljs-attr\">language:</span> <span class=\"hljs-string\">&lt;language&gt;</span> <span class=\"hljs-comment\"># language code (e.g.: `en`, `fr`)</span>\n      <span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">&lt;output&gt;</span> <span class=\"hljs-comment\"># output format (e.g.: `html`, `atom`)</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">subsets:</span>\n    <span class=\"hljs-attr\">blog_en:</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">blog</span>\n      <span class=\"hljs-attr\">language:</span> <span class=\"hljs-string\">en</span>\n      <span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">html</span>\n    <span class=\"hljs-attr\">search_index:</span>\n      <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">'*'</span>\n      <span class=\"hljs-attr\">output:</span> <span class=\"hljs-string\">json</span></code></pre>\n<hr>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/content/multilingual/",
      "url": "https://cecil-945.pages.dev/documentation/content/multilingual/",
      "title": "Multilingual",
      "summary": "Translate pages through file name or front matter and link translated pages.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-03T00:00:00+00:00","content_text": "Multilingual\nIf your pages are available in multiple languages there is 2 different ways to define it:\nThrough file name\nThis is the common way to translate a page from the main language to another language.\nYou just need to duplicate the reference page and suffix it with the target language code (e.g.: fr).\nExample:\n├─ about.md    # the reference page\n└─ about.fr.md # the french version (`fr`)\nYou can change the URL of the translated page with the slug variable in the front matter. For example:\n---\nslug: a-propos\n---\n# about.md    -&gt; \/about\/\n# about.fr.md -&gt; \/fr\/a-propos\/\nThrough front matter\nIf you want to create a page in a language other than the main language, without it being a translation of an existing page, you can use the language variable in its front matter.\nExample:\n---\nlanguage: fr\n---\nLink translated pages\nEach translated page reference the pages in others languages.\nThose pages collection is available in templates with the following variable:\n{{ page.translations }}\nThe langref variable is provided by default, but you can change it in the front matter:\n---\nlangref: my-page-ref\n---",
      "content_html": "<h1>Multilingual</h1>\n<p>If your pages are available in multiple <a href=\"../configuration/2-languages.md#languages\">languages</a> there is 2 different ways to define it:</p>\n<h2 id=\"through-file-name\">Through file name</h2>\n<p>This is the common way to translate a page from the main <a href=\"../configuration/2-languages.md#language\">language</a> to another language.</p>\n<p>You just need to duplicate the reference page and suffix it with the target language <code translate=\"no\">code</code> (e.g.: <code translate=\"no\">fr</code>).</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">├─ about.md    # the reference page\n└─ about.fr.md # the french version (`fr`)</code></pre>\n<aside class=\"note note-tip\"><p>You can change the URL of the translated page with the <code translate=\"no\">slug</code> variable in the front matter. For example:</p>\n<pre><code class=\"language-yml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">slug:</span> <span class=\"hljs-string\">a-propos</span>\n<span class=\"hljs-meta\">---</span>\n<span class=\"hljs-comment\"># about.md    -&gt; /about/</span>\n<span class=\"hljs-comment\"># about.fr.md -&gt; /fr/a-propos/</span></code></pre></aside>\n<h2 id=\"through-front-matter\">Through front matter</h2>\n<p>If you want to create a page in a language other than the main language, without it being a translation of an existing page, you can use the <code translate=\"no\">language</code> variable in its front matter.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">language:</span> <span class=\"hljs-string\">fr</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<h2 id=\"link-translated-pages\">Link translated pages</h2>\n<p>Each translated page reference the pages in others languages.</p>\n<p>Those pages collection is available in <a href=\"../templates/2-variables.md#page\">templates</a> with the following variable:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ page.translations }}</span></code></pre>\n<aside class=\"note note-info\"><p>The <code translate=\"no\">langref</code> variable is provided by default, but you can change it in the front matter:</p>\n<pre><code class=\"language-yml hljs yaml\" translate=\"no\"><span class=\"hljs-meta\">---</span>\n<span class=\"hljs-attr\">langref:</span> <span class=\"hljs-string\">my-page-ref</span>\n<span class=\"hljs-meta\">---</span></code></pre></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/templates/components/",
      "url": "https://cecil-945.pages.dev/documentation/templates/components/",
      "title": "Components",
      "summary": "Create reusable template components.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Components\nCecil provides a components logic to give you the power making reusable template \"units\".\nThe components feature is provided by the Twig components extension created by Giorgio Pogliani.\nComponents syntax\nComponents are just Twig templates stored in the components\/ subdirectory and can be used anywhere in your templates:\n{# \/components\/button.twig #}\n&lt;button {{ attributes.merge({class: 'rounded px-4'}) }}&gt;\n    {{ slot }}\n&lt;\/button&gt;\n\nThe slot variable is any content you will add between the opening and the close tag.\n\nTo reach a component you need to use the dedicated tag x followed by : and the filename of your component without extension:\n{# \/index.twig #}\n{% x:button with {class: 'text-white'} %}\n    &lt;strong&gt;Click me&lt;\/strong&gt;\n{% endx %}\nIt will render:\n&lt;button class=\"text-white rounded px-4\"&gt;\n    &lt;strong&gt;Click me&lt;\/strong&gt;\n&lt;\/button&gt;",
      "content_html": "<h1>Components</h1>\n<p>Cecil provides a components logic to give you the power making reusable template \"units\".</p>\n<aside class=\"note note-info\"><p>The components feature is provided by the <a href=\"https://github.com/giorgiopogliani/twig-components\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Twig components extension</em></a> created by Giorgio Pogliani.</p></aside>\n<h2 id=\"components-syntax\">Components syntax</h2>\n<p>Components are just Twig templates stored in the <code translate=\"no\">components/</code> subdirectory and can be used anywhere in your templates:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# /components/button.twig #}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button</span> </span></span><span class=\"hljs-template-variable\">{{ attributes.merge({class: 'rounded px-4'}) }}</span><span class=\"xml\"><span class=\"hljs-tag\">&gt;</span>\n    </span><span class=\"hljs-template-variable\">{{ slot }}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">button</span>&gt;</span></span></code></pre>\n<blockquote>\n<p>The slot variable is any content you will add between the opening and the close tag.</p>\n</blockquote>\n<p>To reach a component you need to use the dedicated tag <code translate=\"no\">x</code> followed by <code translate=\"no\">:</code> and the filename of your component without extension:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# /index.twig #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">x</span>:button with {class: 'text-white'} %}</span><span class=\"xml\">\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong</span>&gt;</span>Click me<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">strong</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endx</span> %}</span></code></pre>\n<p>It will render:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"text-white rounded px-4\"</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong</span>&gt;</span>Click me<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">strong</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">button</span>&gt;</span></span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/data-static/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/data-static/",
      "title": "Data and static files",
      "summary": "Data files and static files options.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Data and static files\nThese options define where data files and static files are stored and how they are handled.\nData\nWhere data files are stored and what extensions are handled.\nSupported formats: YAML, JSON, XML and CSV.\ndata.dir\nData source directory (data by default).\ndata:\n  dir: data\ndata.ext\nArray of files extensions.\ndata:\n  ext: [yaml, yml, json, xml, csv]\ndata.load\nEnables site.data collection (true by default).\ndata:\n  load: true\n\nStatic\nManagement of static files are copied (PDF, fonts, etc.).\nYou should put your assets files, used by asset(), in the assets directory to avoid unnecessary files copy.\nstatic.dir\nStatic files source directory (static by default).\nstatic:\n  dir: static\nstatic.target\nDirectory where static files are copied (root by default).\nstatic:\n  target: ''\nstatic.exclude\nList of excluded files. Accepts globs, strings and regexes.\nstatic:\n  exclude: ['sass', 'scss', '*.scss', 'package*.json', 'node_modules']\nIf you use Bootstrap Icons you can exclude the node_modules except node_modules\/bootstrap-icons with a regular expression:\nexclude: ['sass', 'scss', '*.scss', 'package*.json', '#node_modules\/(?!bootstrap-icons)#']\nstatic.load\nEnables site.static collection (false by default).\nstatic:\n  load: false\nstatic.mounts\nAllows to copy specific files or directories to a specific destination.\nstatic:\n  mounts: []\nstatic example\nstatic:\n  dir: docs\n  target: docs\n  exclude: ['sass', '*.scss', '\/\\.bck$\/']\n  load: true\n  mounts:\n    - source\/path\/file.ext: dest\/path\/file.ext\n    - node_modules\/bootstrap-icons\/font\/fonts: fonts",
      "content_html": "<h1>Data and static files</h1>\n<p>These options define where data files and static files are stored and how they are handled.</p>\n<h2 id=\"data\">Data</h2>\n<p>Where data files are stored and what extensions are handled.</p>\n<p>Supported formats: YAML, JSON, XML and CSV.</p>\n<h3 id=\"data-dir\">data.dir</h3>\n<p>Data source directory (<code translate=\"no\">data</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">data:</span>\n  <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">data</span></code></pre>\n<h3 id=\"data-ext\">data.ext</h3>\n<p>Array of files extensions.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">data:</span>\n  <span class=\"hljs-attr\">ext:</span> <span class=\"hljs-string\">[yaml,</span> <span class=\"hljs-string\">yml,</span> <span class=\"hljs-string\">json,</span> <span class=\"hljs-string\">xml,</span> <span class=\"hljs-string\">csv]</span></code></pre>\n<h3 id=\"data-load\">data.load</h3>\n<p>Enables <code translate=\"no\">site.data</code> collection (<code translate=\"no\">true</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">data:</span>\n  <span class=\"hljs-attr\">load:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<hr>\n<h2 id=\"static\">Static</h2>\n<p>Management of static files are copied (PDF, fonts, etc.).</p>\n<aside class=\"note note-important\"><p>You should put your assets files, used by <a href=\"../assets/index.md#asset\"><code translate=\"no\">asset()</code></a>, in the <a href=\"6-assets.md#assets-dir\"><code translate=\"no\">assets</code> directory</a> to avoid unnecessary files copy.</p></aside>\n<h3 id=\"static-dir\">static.dir</h3>\n<p>Static files source directory (<code translate=\"no\">static</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">static:</span>\n  <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">static</span></code></pre>\n<h3 id=\"static-target\">static.target</h3>\n<p>Directory where static files are copied (root by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">static:</span>\n  <span class=\"hljs-attr\">target:</span> <span class=\"hljs-string\">''</span></code></pre>\n<h3 id=\"static-exclude\">static.exclude</h3>\n<p>List of excluded files. Accepts globs, strings and regexes.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">static:</span>\n  <span class=\"hljs-attr\">exclude:</span> <span class=\"hljs-string\">['sass',</span> <span class=\"hljs-string\">'scss'</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">'*.scss'</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">'package*.json'</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">'node_modules'</span><span class=\"hljs-string\">]</span></code></pre>\n<aside class=\"note note-tip\"><p>If you use <a href=\"https://icons.getbootstrap.com\" target=\"_blank\" rel=\"noopener noreferrer\">Bootstrap Icons</a> you can exclude the <code translate=\"no\">node_modules</code> except <code translate=\"no\">node_modules/bootstrap-icons</code> with a regular expression:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">exclude:</span> <span class=\"hljs-string\">['sass',</span> <span class=\"hljs-string\">'scss'</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">'*.scss'</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">'package*.json'</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">'#node_modules/(?!bootstrap-icons)#'</span><span class=\"hljs-string\">]</span></code></pre></aside>\n<h3 id=\"static-load\">static.load</h3>\n<p>Enables <code translate=\"no\">site.static</code> collection (<code translate=\"no\">false</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">static:</span>\n  <span class=\"hljs-attr\">load:</span> <span class=\"hljs-literal\">false</span></code></pre>\n<h3 id=\"static-mounts\">static.mounts</h3>\n<p>Allows to copy specific files or directories to a specific destination.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">static:</span>\n  <span class=\"hljs-attr\">mounts:</span> <span class=\"hljs-string\">[]</span></code></pre>\n<h3 id=\"static-example\">static example</h3>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">static:</span>\n  <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">docs</span>\n  <span class=\"hljs-attr\">target:</span> <span class=\"hljs-string\">docs</span>\n  <span class=\"hljs-attr\">exclude:</span> <span class=\"hljs-string\">['sass',</span> <span class=\"hljs-string\">'*.scss'</span><span class=\"hljs-string\">,</span> <span class=\"hljs-string\">'/\\.bck$/'</span><span class=\"hljs-string\">]</span>\n  <span class=\"hljs-attr\">load:</span> <span class=\"hljs-literal\">true</span>\n  <span class=\"hljs-attr\">mounts:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">source/path/file.ext:</span> <span class=\"hljs-string\">dest/path/file.ext</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">node_modules/bootstrap-icons/font/fonts:</span> <span class=\"hljs-string\">fonts</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/content/dynamic-content/",
      "url": "https://cecil-945.pages.dev/documentation/content/dynamic-content/",
      "title": "Dynamic content",
      "summary": "Use variables and Twig expressions inside page content.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-09T00:00:00+00:00","content_text": "Dynamic content\nBy default, the body of a page is static: Twig syntax written in a Markdown file is output as is.\nYou can create dynamic content in a page by using the template_from_string Twig function, which renders the page content as a Twig template.\n{{ include(template_from_string(page.content, \"dynamic content for page \" ~ page.id)) }}\nWith this, you can use any page variable in the body of the page.\n---\nvar: 'value'\n---\nThe value of `var` is {{ page.var }}.\nEnable dynamic content\nThe recommended way is to create a dedicated layout that overrides the content block, and to use it only for pages that need it.\n{# layouts\/dynamic.html.twig #}\n{% extends '_default\/page.html.twig' %}\n\n{% block content %}\n{{ include(template_from_string(page.content, \"dynamic content for page \" ~ page.id)) }}\n{% endblock content %}\nThen set the layout variable in the front matter of the page:\n---\ntitle: \"My dynamic page\"\nlayout: dynamic\n---\nThe second argument of template_from_string is the name of the template: it is displayed in error messages, so including page.id helps to find the page in error.\nAvailable variables\nThe page content is rendered with the same context as the layout, so you can use:\n\npage variables: page.title, page.date, page.&lt;custom variable&gt;, etc.\nsite variables: site.title, site.pages, site.data, etc.\nall functions, filters and sorts available in templates;\nTwig tags: {% set %}, {% if %}, {% for %}, {% include %}, etc.\n\nHow it works\nThe Markdown body is converted to HTML first, then the result (page.content) is rendered by Twig. This order has some consequences:\n\nMarkdown escapes the &lt; and &gt; characters, so comparison operators (&gt;, &lt;=) and arrow functions (=&gt;) can’t be used in the body: the build fails with an Unexpected character \"&amp;\" error.\nA Twig expression used in an HTML attribute (e.g. href=\"{{ url(post) }}\") is URL encoded by the Markdown converter and won’t be evaluated.\nA Twig expression alone on its own line is wrapped in a &lt;p&gt; element.\nTwig syntax written inside a code span or a code block is evaluated too.\n\nKeep the body simple (variables, filters, short conditions) and move complex markup and logic into a partial template or a macro.\nTo display Twig syntax as is, wrap it in a verbatim tag:\n{% verbatim %}`{{ page.title }}`{% endverbatim %}\nTo avoid an extra &lt;p&gt; element around a block of HTML, wrap the expression in an HTML element, separated by line breaks:\n&lt;div&gt;\n{{ include('partials\/latest-posts.html.twig') }}\n&lt;\/div&gt;\nExample: variables, data and partial template\nThis example shows a page displaying site and page variables, data from a data file, and the latest blog posts through a reusable partial template.\nData file:\n# data\/team.yml\n- name: Alice\n  role: Developer\n- name: Bob\n  role: Designer\nPartial template, which receives section and limit as parameters:\n{# layouts\/partials\/latest-posts.html.twig #}\n{% set posts = site.pages.showable|filter_by('section', section|default('blog'))|sort_by_date|slice(0, limit|default(5)) %}\n{% if posts|length &gt; 0 %}\n&lt;ul class=\"latest-posts\"&gt;\n  {% for post in posts %}\n  &lt;li&gt;&lt;a href=\"{{ url(post) }}\"&gt;{{ post.title }}&lt;\/a&gt; &lt;time datetime=\"{{ post.date|date('Y-m-d') }}\"&gt;{{ post.date|format_date('long') }}&lt;\/time&gt;&lt;\/li&gt;\n  {% endfor %}\n&lt;\/ul&gt;\n{% else %}\n&lt;p&gt;No posts yet.&lt;\/p&gt;\n{% endif %}\nPage:\n---\ntitle: About\nlayout: dynamic\n---\nWelcome to **{{ site.title }}**! This page was published on {{ page.date|format_date('long') }}.\n\nOur team has {{ site.data.team|length }} members: {{ site.data.team|column('name')|join(', ') }}.\n\n## Latest posts\n\n&lt;div&gt;\n{{ include('partials\/latest-posts.html.twig', {section: 'blog', limit: 3}) }}\n&lt;\/div&gt;\nOutput:\n&lt;p&gt;Welcome to &lt;strong&gt;My site&lt;\/strong&gt;! This page was published on October 9, 2026.&lt;\/p&gt;\n&lt;p&gt;Our team has 2 members: Alice, Bob.&lt;\/p&gt;\n&lt;h2 id=\"latest-posts\"&gt;Latest posts&lt;\/h2&gt;\n&lt;div&gt;\n&lt;ul class=\"latest-posts\"&gt;\n  &lt;li&gt;&lt;a href=\"\/blog\/post-3\/\"&gt;Post 3&lt;\/a&gt; &lt;time datetime=\"2026-03-01\"&gt;March 1, 2026&lt;\/time&gt;&lt;\/li&gt;\n  &lt;li&gt;&lt;a href=\"\/blog\/post-2\/\"&gt;Post 2&lt;\/a&gt; &lt;time datetime=\"2026-02-01\"&gt;February 1, 2026&lt;\/time&gt;&lt;\/li&gt;\n  &lt;li&gt;&lt;a href=\"\/blog\/post-1\/\"&gt;Post 1&lt;\/a&gt; &lt;time datetime=\"2026-01-01\"&gt;January 1, 2026&lt;\/time&gt;&lt;\/li&gt;\n&lt;\/ul&gt;\n&lt;\/div&gt;\nThe &gt; operator and the href=\"{{ … }}\" attribute work here because they are written in the partial template, not in the Markdown body.\nExample: shortcodes with macros\nTwig macros can be used as shortcodes to insert rich HTML snippets in the body of a page.\nCreate the macros:\n{# layouts\/macros\/shortcodes.html.twig #}\n{% macro youtube(id, title = 'YouTube video') %}\n&lt;iframe src=\"https:\/\/www.youtube-nocookie.com\/embed\/{{ id }}\" title=\"{{ title }}\" width=\"560\" height=\"315\" loading=\"lazy\" allowfullscreen&gt;&lt;\/iframe&gt;\n{% endmacro %}\n\n{% macro alert(message, type = 'info') %}\n&lt;div class=\"alert alert-{{ type }}\" role=\"alert\"&gt;{{ message }}&lt;\/div&gt;\n{% endmacro %}\nCreate a layout that imports the macros before rendering the content:\n{# layouts\/shortcodes.html.twig #}\n{% extends '_default\/page.html.twig' %}\n\n{% block content %}\n{% set imports = \"{% import 'macros\/shortcodes.html.twig' as sc %}\" %}\n{{ include(template_from_string(imports ~ page.content, \"dynamic content for page \" ~ page.id)) }}\n{% endblock content %}\nUse the shortcodes in a page:\n---\ntitle: \"Demo video\"\nlayout: shortcodes\n---\n&lt;div&gt;\n  {{ sc.alert('This video is in English.', 'warning') }}\n&lt;\/div&gt;\n\n## Demo\n\n&lt;div&gt;\n{{ sc.youtube('NaB8JBfE7DY', 'Cecil demo') }}\n&lt;\/div&gt;\nOutput:\n&lt;div&gt;\n  &lt;div class=\"alert alert-warning\" role=\"alert\"&gt;This video is in English.&lt;\/div&gt;\n&lt;\/div&gt;\n&lt;h2 id=\"demo\"&gt;Demo&lt;\/h2&gt;\n&lt;div&gt;\n  &lt;iframe src=\"https:\/\/www.youtube-nocookie.com\/embed\/NaB8JBfE7DY\" title=\"Cecil demo\" width=\"560\" height=\"315\" loading=\"lazy\" allowfullscreen&gt;&lt;\/iframe&gt;\n&lt;\/div&gt;\nTo reuse UI elements across templates and pages, see also components.",
      "content_html": "<h1>Dynamic content</h1>\n<p>By default, the body of a page is static: Twig syntax written in a Markdown file is output as is.</p>\n<p>You can create dynamic content in a page by using the <a href=\"https://twig.symfony.com/doc/3.x/functions/template_from_string.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">template_from_string</code></a> Twig function, which renders the page content as a Twig template.</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\">(template_from_string(page.content, \"dynamic content for page \" ~ page.id)</span>) }}</span></code></pre>\n<p>With this, you can use any page variable in the <em>body</em> of the page.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\">---\nvar: 'value'\n---\nThe value of `var` is </span><span class=\"hljs-template-variable\">{{ page.var }}</span><span class=\"xml\">.</span></code></pre>\n<h2 id=\"enable-dynamic-content\">Enable dynamic content</h2>\n<p>The recommended way is to create a dedicated layout that overrides the <code translate=\"no\">content</code> block, and to use it only for pages that need it.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# layouts/dynamic.html.twig #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">extends</span></span> '_default/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><span class=\"hljs-template-variable\">{{ <span class=\"hljs-name\">include</span><span class=\"hljs-params\">(template_from_string(page.content, \"dynamic content for page \" ~ page.id)</span>) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endblock</span></span> content %}</span></code></pre>\n<p>Then set the <a href=\"2-front-matter.md#predefined-variables\"><code translate=\"no\">layout</code></a> variable in the front matter 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\">\"My dynamic page\"</span>\n<span class=\"hljs-attr\">layout:</span> <span class=\"hljs-string\">dynamic</span>\n<span class=\"hljs-meta\">---</span></code></pre>\n<aside class=\"note note-tip\"><p>The second argument of <code translate=\"no\">template_from_string</code> is the name of the template: it is displayed in error messages, so including <code translate=\"no\">page.id</code> helps to find the page in error.</p></aside>\n<h2 id=\"available-variables\">Available variables</h2>\n<p>The page content is rendered with the same context as the layout, so you can use:</p>\n<ul>\n<li>page variables: <code translate=\"no\">page.title</code>, <code translate=\"no\">page.date</code>, <code translate=\"no\">page.&lt;custom variable&gt;</code>, etc.</li>\n<li>site variables: <code translate=\"no\">site.title</code>, <code translate=\"no\">site.pages</code>, <a href=\"../templates/2-variables.md#site-data\"><code translate=\"no\">site.data</code></a>, etc.</li>\n<li>all <a href=\"../templates/reference/1-functions.md\">functions</a>, <a href=\"../templates/reference/3-filters.md\">filters</a> and <a href=\"../templates/reference/2-sorts.md\">sorts</a> available in templates;</li>\n<li>Twig tags: <code translate=\"no\">{% set %}</code>, <code translate=\"no\">{% if %}</code>, <code translate=\"no\">{% for %}</code>, <code translate=\"no\">{% include %}</code>, etc.</li>\n</ul>\n<h2 id=\"how-it-works\">How it works</h2>\n<p>The Markdown body is <strong>converted to HTML first</strong>, then the result (<code translate=\"no\">page.content</code>) is rendered by Twig. This order has some consequences:</p>\n<ul>\n<li>Markdown escapes the <code translate=\"no\">&lt;</code> and <code translate=\"no\">&gt;</code> characters, so comparison operators (<code translate=\"no\">&gt;</code>, <code translate=\"no\">&lt;=</code>) and arrow functions (<code translate=\"no\">=&gt;</code>) can’t be used in the body: the build fails with an <code translate=\"no\">Unexpected character \"&amp;\"</code> error.</li>\n<li>A Twig expression used in an HTML attribute (e.g. <code translate=\"no\">href=\"{{ url(post) }}\"</code>) is URL encoded by the Markdown converter and won’t be evaluated.</li>\n<li>A Twig expression alone on its own line is wrapped in a <code translate=\"no\">&lt;p&gt;</code> element.</li>\n<li>Twig syntax written inside a code span or a code block <strong>is</strong> evaluated too.</li>\n</ul>\n<aside class=\"note note-tip\"><p>Keep the body simple (variables, filters, short conditions) and move complex markup and logic into a <a href=\"#example-include-a-partial-template\">partial template</a> or a <a href=\"#example-shortcodes-with-macros\">macro</a>.</p></aside>\n<p>To display Twig syntax as is, wrap it in a <code translate=\"no\">verbatim</code> tag:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">verbatim</span></span> %}</span><span class=\"xml\">`</span><span class=\"hljs-template-variable\">{{ page.title }}</span><span class=\"xml\">`</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endverbatim</span></span> %}</span></code></pre>\n<p>To avoid an extra <code translate=\"no\">&lt;p&gt;</code> element around a block of HTML, wrap the expression in an HTML element, separated by line breaks:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span>&gt;</span>\n</span><span class=\"hljs-template-variable\">{{ <span class=\"hljs-name\">include</span><span class=\"hljs-params\">('partials/latest-posts.html.twig')</span> }}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span></span></code></pre>\n<h2 id=\"example-include-a-partial-template\">Example: variables, data and partial template</h2>\n<p>This example shows a page displaying site and page variables, data from a <a href=\"../templates/2-variables.md#site-data\">data file</a>, and the latest blog posts through a reusable partial template.</p>\n<p>Data file:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-comment\"># data/team.yml</span>\n<span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Alice</span>\n  <span class=\"hljs-attr\">role:</span> <span class=\"hljs-string\">Developer</span>\n<span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Bob</span>\n  <span class=\"hljs-attr\">role:</span> <span class=\"hljs-string\">Designer</span></code></pre>\n<p>Partial template, which receives <code translate=\"no\">section</code> and <code translate=\"no\">limit</code> as parameters:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# layouts/partials/latest-posts.html.twig #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> posts = site.pages.showable|filter_by('section', section|<span class=\"hljs-keyword\">default</span>('blog'))|sort_by_date|<span class=\"hljs-keyword\">slice</span>(0, limit|<span class=\"hljs-keyword\">default</span>(5)) %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">if</span></span> posts|<span class=\"hljs-keyword\">length</span> &gt; 0 %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"latest-posts\"</span>&gt;</span>\n  </span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">for</span></span> post in posts %}</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(post) }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ post.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\">time</span> <span class=\"hljs-attr\">datetime</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ post.<span class=\"hljs-name\">date</span>|<span class=\"hljs-keyword\">date</span>('Y-m-d') }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ post.<span class=\"hljs-name\">date</span>|format_date('long') }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">time</span>&gt;</span><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\">else</span> %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p</span>&gt;</span>No posts yet.<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">p</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>Page:</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">---\ntitle: About\n<span class=\"hljs-section\">layout: dynamic\n---</span>\nWelcome to <span class=\"hljs-strong\">**{{ site.title }}**</span>! This page was published on {{ page.date|format_date('long') }}.\n\nOur team has {{ site.data.team|length }} members: {{ site.data.team|column('name')|join(', ') }}.\n\n<span class=\"hljs-section\">## Latest posts</span>\n\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span>&gt;</span></span>\n{{ include('partials/latest-posts.html.twig', {section: 'blog', limit: 3}) }}\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span></span></code></pre>\n<p>Output:</p>\n<pre><code class=\"language-html hljs xml\" translate=\"no\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p</span>&gt;</span>Welcome to <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong</span>&gt;</span>My site<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">strong</span>&gt;</span>! This page was published on October 9, 2026.<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">p</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p</span>&gt;</span>Our team has 2 members: Alice, Bob.<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">p</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2</span> <span class=\"hljs-attr\">id</span>=<span class=\"hljs-string\">\"latest-posts\"</span>&gt;</span>Latest posts<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">h2</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"latest-posts\"</span>&gt;</span>\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\">\"/blog/post-3/\"</span>&gt;</span>Post 3<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">time</span> <span class=\"hljs-attr\">datetime</span>=<span class=\"hljs-string\">\"2026-03-01\"</span>&gt;</span>March 1, 2026<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">time</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">li</span>&gt;</span>\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\">\"/blog/post-2/\"</span>&gt;</span>Post 2<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">time</span> <span class=\"hljs-attr\">datetime</span>=<span class=\"hljs-string\">\"2026-02-01\"</span>&gt;</span>February 1, 2026<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">time</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">li</span>&gt;</span>\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\">\"/blog/post-1/\"</span>&gt;</span>Post 1<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">a</span>&gt;</span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">time</span> <span class=\"hljs-attr\">datetime</span>=<span class=\"hljs-string\">\"2026-01-01\"</span>&gt;</span>January 1, 2026<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">time</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">li</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">ul</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span></code></pre>\n<aside class=\"note note-info\"><p>The <code translate=\"no\">&gt;</code> operator and the <code translate=\"no\">href=\"{{ … }}\"</code> attribute work here because they are written in the partial template, not in the Markdown body.</p></aside>\n<h2 id=\"example-shortcodes-with-macros\">Example: shortcodes with macros</h2>\n<p><a href=\"https://twig.symfony.com/doc/3.x/tags/macro.html\" target=\"_blank\" rel=\"noopener noreferrer\">Twig macros</a> can be used as <em>shortcodes</em> to insert rich HTML snippets in the body of a page.</p>\n<p>Create the macros:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# layouts/macros/shortcodes.html.twig #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">macro</span></span> youtube(id, title = 'YouTube video') %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">iframe</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"https://www.youtube-nocookie.com/embed/</span></span></span><span class=\"hljs-template-variable\">{{ id }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">title</span>=<span class=\"hljs-string\">\"</span></span></span><span class=\"hljs-template-variable\">{{ title }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"560\"</span> <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"315\"</span> <span class=\"hljs-attr\">loading</span>=<span class=\"hljs-string\">\"lazy\"</span> <span class=\"hljs-attr\">allowfullscreen</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">iframe</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endmacro</span></span> %}</span><span class=\"xml\">\n\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">macro</span></span> alert(message, type = 'info') %}</span><span class=\"xml\">\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"alert alert-</span></span></span><span class=\"hljs-template-variable\">{{ type }}</span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"</span> <span class=\"hljs-attr\">role</span>=<span class=\"hljs-string\">\"alert\"</span>&gt;</span></span><span class=\"hljs-template-variable\">{{ message }}</span><span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span>\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endmacro</span></span> %}</span></code></pre>\n<p>Create a layout that imports the macros before rendering the content:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# layouts/shortcodes.html.twig #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">extends</span></span> '_default/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><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> imports = \"{% import 'macros/shortcodes.html.twig' as sc %}</span><span class=\"xml\">\" %}\n</span><span class=\"hljs-template-variable\">{{ <span class=\"hljs-name\">include</span><span class=\"hljs-params\">(template_from_string(imports ~ page.content, \"dynamic content for page \" ~ page.id)</span>) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endblock</span></span> content %}</span></code></pre>\n<p>Use the shortcodes in a page:</p>\n<pre><code class=\"language-markdown hljs markdown\" translate=\"no\">---\ntitle: \"Demo video\"\n<span class=\"hljs-section\">layout: shortcodes\n---</span>\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span>&gt;</span></span>\n  {{ sc.alert('This video is in English.', 'warning') }}\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span></span>\n\n<span class=\"hljs-section\">## Demo</span>\n\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span>&gt;</span></span>\n{{ sc.youtube('NaB8JBfE7DY', 'Cecil demo') }}\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span></span></code></pre>\n<p>Output:</p>\n<pre><code class=\"language-html hljs xml\" translate=\"no\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span> <span class=\"hljs-attr\">class</span>=<span class=\"hljs-string\">\"alert alert-warning\"</span> <span class=\"hljs-attr\">role</span>=<span class=\"hljs-string\">\"alert\"</span>&gt;</span>This video is in English.<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2</span> <span class=\"hljs-attr\">id</span>=<span class=\"hljs-string\">\"demo\"</span>&gt;</span>Demo<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">h2</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div</span>&gt;</span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">iframe</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"https://www.youtube-nocookie.com/embed/NaB8JBfE7DY\"</span> <span class=\"hljs-attr\">title</span>=<span class=\"hljs-string\">\"Cecil demo\"</span> <span class=\"hljs-attr\">width</span>=<span class=\"hljs-string\">\"560\"</span> <span class=\"hljs-attr\">height</span>=<span class=\"hljs-string\">\"315\"</span> <span class=\"hljs-attr\">loading</span>=<span class=\"hljs-string\">\"lazy\"</span> <span class=\"hljs-attr\">allowfullscreen</span>&gt;</span><span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">iframe</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">div</span>&gt;</span></code></pre>\n<aside class=\"note note-tip\"><p>To reuse UI elements across templates and pages, see also <a href=\"../templates/4-components.md\">components</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/templates/localization/",
      "url": "https://cecil-945.pages.dev/documentation/templates/localization/",
      "title": "Localization",
      "summary": "Translate texts and localize dates in templates.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Localization\nCecil support text translation and date localization.\nText translation\nUses the trans tag or filter to translate texts in templates.\n{% trans with variables into locale %}{% endtrans %}\n{{ message|trans(variables = []) }}\nExamples\n{% trans %}Hello World!{% endtrans %}\n{{ message|trans }}\nInclude variables:\n{% trans with {'%name%': 'Arnaud'} %}Hello %name%!{% endtrans %}\n{{ message|trans({'%name%': 'Arnaud'}) }}\nForce locale:\n{% trans into 'fr_FR' %}Hello World!{% endtrans %}\nPluralize:\n{% trans with {'%count%': 42}%}{0}I don't have apples|{1}I have one apple|]1,Inf[I have %count% apples{% endtrans %}\nTranslation files\nTranslation files must be named messages.&lt;locale&gt;.&lt;extension&gt; and stored in the translations directory.\nSupported file extensions are defined by each translation format in layouts.translations.formats.\nThe locale code (e.g.: fr_FR) of a language is defined in the languages entries of the configuration.\nExample:\n&lt;mywebsite&gt;\n└─ translations\n   ├─ messages.fr_FR.mo   &lt;- Machine Object format\n   └─ messages.fr_FR.yaml &lt;- Yaml format\nYou can easily extract translations from your templates with the following command:\nphp cecil.phar util:translations:extract --locale=&lt;code&gt; --show\nUse --save instead of (or in addition to) --show to save the translations to a file. The --locale option is required. The default output format is yaml (use --format=po for gettext PO format).\nPoedit is a simple and cross platform translation editor for gettext (PO), and Poedit Pro supports extraction of translation strings from templates out of the box.\nBe careful about the cache when you update translations files.\nCache can be cleared with with the following command:\nphp cecil.phar cache:clear:translations`\nDate localization\nUses the Twig format_date filter to localize a date in templates.\n{{ page.date|format_date('long') }}\n{# September 30, 2022 #}\nSupported values are: short, medium, long, and full.\nIf you want to use the format_date filter with other locales than \"en\", you should install the intl PHP extension.",
      "content_html": "<h1>Localization</h1>\n<p>Cecil support <a href=\"#text-translation\">text translation</a> and <a href=\"#date-localization\">date localization</a>.</p>\n<h2 id=\"text-translation\">Text translation</h2>\n<p>Uses the <code translate=\"no\">trans</code> <em>tag</em> or <em>filter</em> to translate texts in templates.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">trans</span> with variables into locale %}</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endtrans</span> %}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ message|trans(variables = []) }}</span></code></pre>\n<h3 id=\"examples\">Examples</h3>\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\">Hello World!</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endtrans</span> %}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ message|trans }}</span></code></pre>\n<p>Include variables:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">trans</span> with {'%name%': 'Arnaud'} %}</span><span class=\"xml\">Hello %name%!</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endtrans</span> %}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ message|trans({'%name%': 'Arnaud'}) }}</span></code></pre>\n<p>Force locale:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">trans</span> into 'fr_FR' %}</span><span class=\"xml\">Hello World!</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endtrans</span> %}</span></code></pre>\n<p>Pluralize:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">trans</span> with {'%count%': 42}%}</span><span class=\"xml\">{0}I don't have apples|{1}I have one apple|]1,Inf[I have %count% apples</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endtrans</span> %}</span></code></pre>\n<h2 id=\"translation-files\">Translation files</h2>\n<p>Translation files must be named <code translate=\"no\">messages.&lt;locale&gt;.&lt;extension&gt;</code> and stored in the <a href=\"../configuration/7-layouts.md\"><code translate=\"no\">translations</code></a> directory.<br>\nSupported file extensions are defined by each translation format in <a href=\"../configuration/7-layouts.md#layouts-translations\"><code translate=\"no\">layouts.translations.formats</code></a>.</p>\n<p>The locale code (e.g.: <code translate=\"no\">fr_FR</code>) of a language is defined in the <a href=\"../configuration/2-languages.md#languages\"><code translate=\"no\">languages</code></a> entries of the configuration.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">&lt;mywebsite&gt;\n└─ translations\n   ├─ messages.fr_FR.mo   &lt;- Machine Object format\n   └─ messages.fr_FR.yaml &lt;- Yaml format</code></pre>\n<aside class=\"note note-info\"><p>You can easily extract translations from your templates with the following command:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar util:translations:extract --locale=&lt;code&gt; --show</code></pre>\n<p>Use <code translate=\"no\">--save</code> instead of (or in addition to) <code translate=\"no\">--show</code> to save the translations to a file. The <code translate=\"no\">--locale</code> option is required. The default output format is <code translate=\"no\">yaml</code> (use <code translate=\"no\">--format=po</code> for gettext PO format).</p></aside>\n<aside class=\"note note-tip\"><p><a href=\"https://poedit.net\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Poedit</em></a> is a simple and cross platform translation editor for gettext (PO), and <a href=\"https://poedit.net/pro\" target=\"_blank\" rel=\"noopener noreferrer\"><em>Poedit Pro</em></a> supports extraction of translation strings from templates out of the box.</p></aside>\n<aside class=\"note note-important\"><p>Be careful about the <a href=\"6-cache.md\">cache</a> when you update translations files.</p>\n<p>Cache can be cleared with with the following command:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar cache:clear:translations`</code></pre></aside>\n<h2 id=\"date-localization\">Date localization</h2>\n<p>Uses the Twig <a href=\"https://twig.symfony.com/doc/3.x/filters/format_date.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">format_date</code></a> filter to localize a date in templates.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ page.<span class=\"hljs-name\">date</span>|format_date('long') }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# September 30, 2022 #}</span></code></pre>\n<p>Supported values are: <code translate=\"no\">short</code>, <code translate=\"no\">medium</code>, <code translate=\"no\">long</code>, and <code translate=\"no\">full</code>.</p>\n<aside class=\"note note-important\"><p>If you want to use the <code translate=\"no\">format_date</code> filter <strong>with other locales than \"en\"</strong>, you should <a href=\"https://php.net/intl.setup\" target=\"_blank\" rel=\"noopener noreferrer\">install the intl PHP extension</a>.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/assets/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/assets/",
      "title": "Assets",
      "summary": "Assets directory, compilation, minification, images and CDN.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Assets\nAssets management (images, CSS and JS files).\nassets.dir\nAssets source directory (assets by default).\nassets:\n  dir: assets\nassets.target\nDirectory where remote and resized assets files are saved (root by default).\nassets:\n  target: ''\nassets.fingerprint\nEnables fingerprinting (cache busting) for assets files (true by default).\nassets:\n  fingerprint: true\nassets.compile\nEnables Sass files compilation (true by default). See the documentation of scssphp for options details.\nassets:\n  compile:\n    style: expanded      # compilation style (`expanded` or `compressed`. `expanded` by default)\n    import: [sass, scss] # list of imported paths (`[sass, scss, node_modules]` by default)\n    sourcemap: false     # enables sourcemap in debug mode (`false` by default)\n    variables: []        # list of preset variables (empty by default)\nsourcemap is used to debug SCSS compilation (debug mode must be enabled).\nassets.minify\nEnables CSS and JS minification (true by default).\nassets:\n  minify: true\nassets.images\nImages management.\nassets:\n  images:\n    optimize: false # enables images optimization with JpegOptim, Optipng, Pngquant 2, SVGO 1, Gifsicle, cwebp, avifenc (`false` by default)\n    quality: 75     # image quality of `optimize` and `resize` (`75` by default)\n    responsive:\n      widths: [480, 640, 768, 1024, 1366, 1600, 1920] # `srcset` attribute images widths\n      sizes:\n        default: '100vw' # default `sizes` attribute (`100vw` by default)\nassets.images.cdn\nURL of image assets can be easily replaced by a provided CDN url.\nassets:\n  images:\n    cdn:\n      enabled: false  # enables Image CDN (`false` by default)\n      canonical: true # `image_url` is canonical (instead of a relative path) (`true` by default)\n      remote: true    # handles not local images too (`true` by default)\n      account: 'xxxx' # provider account\n      url: 'https:\/\/provider.tld\/%account%\/%image_url%?w=%width%&amp;q=%quality%&amp;format=%format%'\nurl is a pattern that contains variables:\n\n%account% replaced by the assets.images.cdn.account option\n%image_url% replaced by the image canonical URL or path\n%width% replaced by the image width\n%quality% replaced by the assets.images.quality option\n%format% replaced by the image format\n\nSee CDN providers.\nassets.remote.useragent\nUser agent used to download remote assets.\nassets:\n  remote:\n    useragent:\n      default: &lt;string&gt; # default user agent\n      useragent1: &lt;string&gt;\n      useragent2: &lt;string&gt;",
      "content_html": "<h1>Assets</h1>\n<p>Assets management (images, CSS and JS files).</p>\n<h2 id=\"assets-dir\">assets.dir</h2>\n<p>Assets source directory (<code translate=\"no\">assets</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">assets:</span>\n  <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">assets</span></code></pre>\n<h2 id=\"assets-target\">assets.target</h2>\n<p>Directory where remote and resized assets files are saved (root by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">assets:</span>\n  <span class=\"hljs-attr\">target:</span> <span class=\"hljs-string\">''</span></code></pre>\n<h2 id=\"assets-fingerprint\">assets.fingerprint</h2>\n<p>Enables fingerprinting (cache busting) for assets files (<code translate=\"no\">true</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">assets:</span>\n  <span class=\"hljs-attr\">fingerprint:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<h2 id=\"assets-compile\">assets.compile</h2>\n<p>Enables <a href=\"https://sass-lang.com\" target=\"_blank\" rel=\"noopener noreferrer\">Sass</a> files compilation (<code translate=\"no\">true</code> by default). See the <a href=\"https://scssphp.github.io/scssphp/docs/#output-formatting\" target=\"_blank\" rel=\"noopener noreferrer\">documentation of scssphp</a> for options details.</p>\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\">expanded</span>      <span class=\"hljs-comment\"># compilation style (`expanded` or `compressed`. `expanded` by default)</span>\n    <span class=\"hljs-attr\">import:</span> <span class=\"hljs-string\">[sass,</span> <span class=\"hljs-string\">scss]</span> <span class=\"hljs-comment\"># list of imported paths (`[sass, scss, node_modules]` by default)</span>\n    <span class=\"hljs-attr\">sourcemap:</span> <span class=\"hljs-literal\">false</span>     <span class=\"hljs-comment\"># enables sourcemap in debug mode (`false` by default)</span>\n    <span class=\"hljs-attr\">variables:</span> <span class=\"hljs-string\">[]</span>        <span class=\"hljs-comment\"># list of preset variables (empty by default)</span></code></pre>\n<aside class=\"note note-info\"><p><code translate=\"no\">sourcemap</code> is used to debug SCSS compilation (<a href=\"1-site.md#debug\">debug mode</a> must be enabled).</p></aside>\n<h2 id=\"assets-minify\">assets.minify</h2>\n<p>Enables CSS and JS minification (<code translate=\"no\">true</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">assets:</span>\n  <span class=\"hljs-attr\">minify:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<h2 id=\"assets-images\">assets.images</h2>\n<p>Images management.</p>\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\">optimize:</span> <span class=\"hljs-literal\">false</span> <span class=\"hljs-comment\"># enables images optimization with JpegOptim, Optipng, Pngquant 2, SVGO 1, Gifsicle, cwebp, avifenc (`false` by default)</span>\n    <span class=\"hljs-attr\">quality:</span> <span class=\"hljs-number\">75</span>     <span class=\"hljs-comment\"># image quality of `optimize` and `resize` (`75` by default)</span>\n    <span class=\"hljs-attr\">responsive:</span>\n      <span class=\"hljs-attr\">widths:</span> <span class=\"hljs-string\">[480,</span> <span class=\"hljs-number\">640</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">768</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">1024</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">1366</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">1600</span><span class=\"hljs-string\">,</span> <span class=\"hljs-number\">1920</span><span class=\"hljs-string\">]</span> <span class=\"hljs-comment\"># `srcset` attribute images widths</span>\n      <span class=\"hljs-attr\">sizes:</span>\n        <span class=\"hljs-attr\">default:</span> <span class=\"hljs-string\">'100vw'</span> <span class=\"hljs-comment\"># default `sizes` attribute (`100vw` by default)</span></code></pre>\n<h2 id=\"assets-images-cdn\">assets.images.cdn</h2>\n<p>URL of image assets can be easily replaced by a provided CDN <code translate=\"no\">url</code>.</p>\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\">cdn:</span>\n      <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">false</span>  <span class=\"hljs-comment\"># enables Image CDN (`false` by default)</span>\n      <span class=\"hljs-attr\">canonical:</span> <span class=\"hljs-literal\">true</span> <span class=\"hljs-comment\"># `image_url` is canonical (instead of a relative path) (`true` by default)</span>\n      <span class=\"hljs-attr\">remote:</span> <span class=\"hljs-literal\">true</span>    <span class=\"hljs-comment\"># handles not local images too (`true` by default)</span>\n      <span class=\"hljs-attr\">account:</span> <span class=\"hljs-string\">'xxxx'</span> <span class=\"hljs-comment\"># provider account</span>\n      <span class=\"hljs-attr\">url:</span> <span class=\"hljs-string\">'https://provider.tld/%account%/%image_url%?w=%width%&amp;q=%quality%&amp;format=%format%'</span></code></pre>\n<p><code translate=\"no\">url</code> is a pattern that contains variables:</p>\n<ul>\n<li><code translate=\"no\">%account%</code> replaced by the <code translate=\"no\">assets.images.cdn.account</code> option</li>\n<li><code translate=\"no\">%image_url%</code> replaced by the image canonical URL or <code translate=\"no\">path</code></li>\n<li><code translate=\"no\">%width%</code> replaced by the image width</li>\n<li><code translate=\"no\">%quality%</code> replaced by the <code translate=\"no\">assets.images.quality</code> option</li>\n<li><code translate=\"no\">%format%</code> replaced by the image format</li>\n</ul>\n<p>See <a href=\"../assets/3-cdn-providers.md\"><strong>CDN providers</strong></a>.</p>\n<h2 id=\"assets-remote-useragent\">assets.remote.useragent</h2>\n<p>User agent used to download remote assets.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">assets:</span>\n  <span class=\"hljs-attr\">remote:</span>\n    <span class=\"hljs-attr\">useragent:</span>\n      <span class=\"hljs-attr\">default:</span> <span class=\"hljs-string\">&lt;string&gt;</span> <span class=\"hljs-comment\"># default user agent</span>\n      <span class=\"hljs-attr\">useragent1:</span> <span class=\"hljs-string\">&lt;string&gt;</span>\n      <span class=\"hljs-attr\">useragent2:</span> <span class=\"hljs-string\">&lt;string&gt;</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/templates/cache/",
      "url": "https://cecil-945.pages.dev/documentation/templates/cache/",
      "title": "Cache",
      "summary": "Templates cache and fragments cache.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Cache\nCecil uses a cache system to speed up the generation process, it can be disabled or cleared.\nThere are three cache types involved in template rendering: templates, assets, and translations.\nClear cache\nYou can clear the cache with the following commands:\nphp cecil.phar cache:clear               # clear all caches\nphp cecil.phar cache:clear:assets        # clear assets cache\nphp cecil.phar cache:clear:templates     # clear templates cache\nphp cecil.phar cache:clear:translations  # clear translations cache\nIn practice you don't need to clear the cache manually, Cecil does it for you when needed (e.g. when files change).\nFragments cache\nCecil provides a way to cache parts of templates rendering to avoid re-rendering the same partial content multiple times.\nTo use fragments cache, you must wrap the content you want to cache with the cache tag.\n{% cache 'unique-key' %}\n  {# cacheable content #}\n{% endcache %}\nYou should use the cache_key function to be sure to have a unique cache key for each content you want to cache.\nFragments cache is persistent, so if the cache key is too generic, you may end up with wrong content displayed.\nTo clear fragments cache only, you can use the following command:\nphp cecil.phar cache:clear:templates --fragments\nDisable cache\nYou can disable cache with the configuration.\nDisabling cache can slow down the generation process, so it's not recommended.\nDuring local development, if you need to clear cache before each generation, you can use the following option:\nphp cecil.phar serve --clear-cache          # clear all caches\nphp cecil.phar serve --clear-cache=&lt;regex&gt;  # clear cache for cache key matches with the regular expression &lt;regex&gt;\nExample:\nphp cecil.phar serve --clear-cache=css  # clear cache for all CSS files",
      "content_html": "<h1>Cache</h1>\n<p>Cecil uses a cache system to speed up the generation process, it can be disabled or cleared.</p>\n<p>There are three cache types involved in template rendering: templates, <a href=\"../assets/index.md#asset\">assets</a>, and <a href=\"5-localization.md#translation-files\">translations</a>.</p>\n<h2 id=\"clear-cache\">Clear cache</h2>\n<p>You can clear the cache with the following commands:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar cache:clear               <span class=\"hljs-comment\"># clear all caches</span>\nphp cecil.phar cache:clear:assets        <span class=\"hljs-comment\"># clear assets cache</span>\nphp cecil.phar cache:clear:templates     <span class=\"hljs-comment\"># clear templates cache</span>\nphp cecil.phar cache:clear:translations  <span class=\"hljs-comment\"># clear translations cache</span></code></pre>\n<aside class=\"note note-important\"><p>In practice you don't need to clear the cache manually, Cecil does it for you when needed (e.g. when files change).</p></aside>\n<h2 id=\"fragments-cache\">Fragments cache</h2>\n<p>Cecil provides a way to cache parts of templates rendering to avoid re-rendering the same partial content multiple times.</p>\n<p>To use <em>fragments</em> cache, you must wrap the content you want to cache with the <a href=\"https://twig.symfony.com/doc/tags/cache.html\" target=\"_blank\" rel=\"noopener noreferrer\"><code translate=\"no\">cache</code> tag</a>.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">cache</span> 'unique-key' %}</span><span class=\"xml\">\n  </span><span class=\"hljs-comment\">{# cacheable content #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endcache</span> %}</span></code></pre>\n<aside class=\"note note-tip\"><p>You should use the <a href=\"reference/1-functions.md#cache-key\"><code translate=\"no\">cache_key</code> function</a> to be sure to have a unique cache key for each content you want to cache.</p></aside>\n<aside class=\"note note-warning\"><p><em>Fragments</em> cache is persistent, so if the cache key is too generic, you may end up with wrong content displayed.</p></aside>\n<p>To clear fragments cache only, you can use the following command:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar cache:clear:templates --fragments</code></pre>\n<h2 id=\"disable-cache\">Disable cache</h2>\n<p>You can disable cache with the <a href=\"../configuration/9-cache.md\">configuration</a>.</p>\n<aside class=\"note note-warning\"><p>Disabling cache can slow down the generation process, so it's not recommended.</p>\n<p>During local development, if you need to clear cache before each generation, you can use the following option:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar serve --clear-cache          <span class=\"hljs-comment\"># clear all caches</span>\nphp cecil.phar serve --clear-cache=&lt;regex&gt;  <span class=\"hljs-comment\"># clear cache for cache key matches with the regular expression &lt;regex&gt;</span></code></pre>\n<p>Example:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar serve --clear-cache=css  <span class=\"hljs-comment\"># clear cache for all CSS files</span></code></pre></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/layouts/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/layouts/",
      "title": "Layouts",
      "summary": "Templates directory, auto-escaping, images, translations and components.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Layouts\nTemplates options.\nlayouts.dir\nTemplates directory source (layouts by default).\nlayouts:\n  dir: layouts\nlayouts.autoescape\nOverrides Twig autoescape option (false by default).\nIf set to null, Cecil uses an extension-based strategy:\n\n*.js.twig -&gt; js\n*.css.twig -&gt; css\n*.html.twig and *.twig -&gt; html\nany other extension -&gt; false\n\nlayouts:\n  autoescape: false  # disables automatic escaping (default) \n  #autoescape: null  # use Cecil automatic strategy by template filename extension \n  #autoescape: html\n  #autoescape: js\nlayouts.images\nImages handling options.\nlayouts:\n  images:\n    formats: []       # used by `html` function: adds alternatives image formats as `source` (e.g. `[avif, webp]`, empty array by default)\n    responsive: false # used by `html` function: adds responsive images ('width' or 'density', `false` by default)\n    placeholder: ''   # used by `html` function: fills image background before loading (`color` or `lqip`, disabled by default)\n    dark_suffix: ''   # suffix of the dark variant image (e.g. `.dark`), disabled by default\n    mobile_suffix: '' # suffix of the mobile variant image (e.g. `.mobile`), disabled by default\n    mobile_media_query: '(max-width: 767px)' # media query of the mobile variant `&lt;source&gt;`\nlayouts.translations\nTranslations handling options.\nlayouts:\n  translations:\n    dir: translations # translations source directory (`translations` by default)\n    formats:          # translations supported formats\n      yaml:\n        loader: Symfony\\Component\\Translation\\Loader\\YamlFileLoader\n        ext: [yml, yaml]\n      mo:\n        loader: Symfony\\Component\\Translation\\Loader\\MoFileLoader\n        ext: [mo]\nEach translation format defines:\n\nloader: Symfony translation loader class\next: one or more file extensions associated with this format\n\nlayouts.components\nTemplates Components options.\nlayouts:\n  components:\n    dir: components # components source directory (`components` by default)\n    ext: twig       # components files extension (`twig` by default)\nlayouts.sections\nMaps a section to the layouts of another section: the mapped name is used in place of &lt;section&gt; by the lookup rules of the section and of its pages.\nlayouts:\n  sections:\n    news: blog # the \"news\" section is rendered with `blog\/list.html.twig` and its pages with `blog\/page.html.twig`\nA sub-section already falls back to the layouts of its parent sections: no mapping is needed for that.\n",
      "content_html": "<h1>Layouts</h1>\n<p>Templates options.</p>\n<h2 id=\"layouts-dir\">layouts.dir</h2>\n<p>Templates directory source (<code translate=\"no\">layouts</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">layouts:</span>\n  <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">layouts</span></code></pre>\n<h2 id=\"layouts-autoescape\">layouts.autoescape</h2>\n<p>Overrides Twig <code translate=\"no\">autoescape</code> option (<code translate=\"no\">false</code> by default).</p>\n<p>If set to <code translate=\"no\">null</code>, Cecil uses an extension-based strategy:</p>\n<ul>\n<li><code translate=\"no\">*.js.twig</code> -&gt; <code translate=\"no\">js</code></li>\n<li><code translate=\"no\">*.css.twig</code> -&gt; <code translate=\"no\">css</code></li>\n<li><code translate=\"no\">*.html.twig</code> and <code translate=\"no\">*.twig</code> -&gt; <code translate=\"no\">html</code></li>\n<li>any other extension -&gt; <code translate=\"no\">false</code></li>\n</ul>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">layouts:</span>\n  <span class=\"hljs-attr\">autoescape:</span> <span class=\"hljs-literal\">false</span>  <span class=\"hljs-comment\"># disables automatic escaping (default) </span>\n  <span class=\"hljs-comment\">#autoescape: null  # use Cecil automatic strategy by template filename extension </span>\n  <span class=\"hljs-comment\">#autoescape: html</span>\n  <span class=\"hljs-comment\">#autoescape: js</span></code></pre>\n<h2 id=\"layouts-images\">layouts.images</h2>\n<p>Images handling options.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">layouts:</span>\n  <span class=\"hljs-attr\">images:</span>\n    <span class=\"hljs-attr\">formats:</span> <span class=\"hljs-string\">[]</span>       <span class=\"hljs-comment\"># used by `html` function: adds alternatives image formats as `source` (e.g. `[avif, webp]`, empty array by default)</span>\n    <span class=\"hljs-attr\">responsive:</span> <span class=\"hljs-literal\">false</span> <span class=\"hljs-comment\"># used by `html` function: adds responsive images ('width' or 'density', `false` by default)</span>\n    <span class=\"hljs-attr\">placeholder:</span> <span class=\"hljs-string\">''</span>   <span class=\"hljs-comment\"># used by `html` function: fills image background before loading (`color` or `lqip`, disabled by default)</span>\n    <span class=\"hljs-attr\">dark_suffix:</span> <span class=\"hljs-string\">''</span>   <span class=\"hljs-comment\"># suffix of the dark variant image (e.g. `.dark`), disabled by default</span>\n    <span class=\"hljs-attr\">mobile_suffix:</span> <span class=\"hljs-string\">''</span> <span class=\"hljs-comment\"># suffix of the mobile variant image (e.g. `.mobile`), disabled by default</span>\n    <span class=\"hljs-attr\">mobile_media_query:</span> <span class=\"hljs-string\">'(max-width: 767px)'</span> <span class=\"hljs-comment\"># media query of the mobile variant `&lt;source&gt;`</span></code></pre>\n<h2 id=\"layouts-translations\">layouts.translations</h2>\n<p>Translations handling options.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">layouts:</span>\n  <span class=\"hljs-attr\">translations:</span>\n    <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">translations</span> <span class=\"hljs-comment\"># translations source directory (`translations` by default)</span>\n    <span class=\"hljs-attr\">formats:</span>          <span class=\"hljs-comment\"># translations supported formats</span>\n      <span class=\"hljs-attr\">yaml:</span>\n        <span class=\"hljs-attr\">loader:</span> <span class=\"hljs-string\">Symfony\\Component\\Translation\\Loader\\YamlFileLoader</span>\n        <span class=\"hljs-attr\">ext:</span> <span class=\"hljs-string\">[yml,</span> <span class=\"hljs-string\">yaml]</span>\n      <span class=\"hljs-attr\">mo:</span>\n        <span class=\"hljs-attr\">loader:</span> <span class=\"hljs-string\">Symfony\\Component\\Translation\\Loader\\MoFileLoader</span>\n        <span class=\"hljs-attr\">ext:</span> <span class=\"hljs-string\">[mo]</span></code></pre>\n<p>Each translation format defines:</p>\n<ul>\n<li><code translate=\"no\">loader</code>: Symfony translation loader class</li>\n<li><code translate=\"no\">ext</code>: one or more file extensions associated with this format</li>\n</ul>\n<h2 id=\"layouts-components\">layouts.components</h2>\n<p><a href=\"../templates/4-components.md\">Templates Components</a> options.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">layouts:</span>\n  <span class=\"hljs-attr\">components:</span>\n    <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">components</span> <span class=\"hljs-comment\"># components source directory (`components` by default)</span>\n    <span class=\"hljs-attr\">ext:</span> <span class=\"hljs-string\">twig</span>       <span class=\"hljs-comment\"># components files extension (`twig` by default)</span></code></pre>\n<h2 id=\"layouts-sections\">layouts.sections</h2>\n<p>Maps a section to the layouts of another section: the mapped name is used in place of <code translate=\"no\">&lt;section&gt;</code> by the <a href=\"../templates/1-lookup-rules.md#lookup-rules\">lookup rules</a> of the section and of its pages.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">layouts:</span>\n  <span class=\"hljs-attr\">sections:</span>\n    <span class=\"hljs-attr\">news:</span> <span class=\"hljs-string\">blog</span> <span class=\"hljs-comment\"># the \"news\" section is rendered with `blog/list.html.twig` and its pages with `blog/page.html.twig`</span></code></pre>\n<aside class=\"note note-tip\"><p>A <a href=\"../content/1-pages.md#sub-section\">sub-section</a> already falls back to the layouts of its parent sections: no mapping is needed for that.</p></aside>\n<hr>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/templates/extend/",
      "url": "https://cecil-945.pages.dev/documentation/templates/extend/",
      "title": "Extend",
      "summary": "Add custom functions and filters, or use a theme.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Extend\nTemplates can be extended with custom functions and filters, or packaged as a reusable theme.\nFunctions and filters\nYou can add custom functions and custom filters with a Twig extension.\nTheme\nIt’s easy to build a theme, you just have to create a folder &lt;theme&gt; with the following structure (like a website but without pages):\n&lt;mywebsite&gt;\n└─ themes\n   └─ &lt;theme&gt;\n      ├─ config.yml\n      ├─ assets\n      ├─ layouts\n      ├─ static\n      └─ translations",
      "content_html": "<h1>Extend</h1>\n<p>Templates can be extended with custom functions and filters, or packaged as a reusable theme.</p>\n<h2 id=\"functions-and-filters\">Functions and filters</h2>\n<p>You can add custom <a href=\"reference/1-functions.md\">functions</a> and custom <a href=\"reference/3-filters.md\">filters</a> with a <a href=\"../developers/1-extend.md#twig-extension\"><strong><em>Twig extension</em></strong></a>.</p>\n<h2 id=\"theme\">Theme</h2>\n<p>It’s easy to build a theme, you just have to create a folder <code translate=\"no\">&lt;theme&gt;</code> with the following structure (like a website but without pages):</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">&lt;mywebsite&gt;\n└─ themes\n   └─ &lt;theme&gt;\n      ├─ config.yml\n      ├─ assets\n      ├─ layouts\n      ├─ static\n      └─ translations</code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/output/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/output/",
      "title": "Output",
      "summary": "Output directory, formats and post-processing.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Output\nDefines where and in what format pages are rendered.\noutput.dir\nDirectory where rendered pages’ files are saved (_site by default).\noutput:\n  dir: _site\noutput.formats\nList of output formats definition, which are used to render pages (e.g. HTML, Atom, RSS, JSON, XML, etc.).\noutput:\n  formats:\n    - name: &lt;name&gt;            # name of the format, e.g.: `html` (required)\n      mediatype: &lt;media type&gt; # media type (MIME type), ie: 'text\/html' (optional)\n      subpath: &lt;sub path&gt;     # sub path, e.g.: `amp` in `path\/amp\/index.html` (optional)\n      filename: &lt;file name&gt;   # file name, e.g.: `index` in `path\/index.html` (optional)\n      extension: &lt;extension&gt;  # file extension, e.g.: `html` in `path\/index.html` (required)\n      exclude: [&lt;variable&gt;]   # don’t apply this format to pages identified by listed variables, e.g.: `[redirect, paginated]` (optional)\nThose formats are used in the output.pagetypeformats configuration and in the output page variable.\nDefault formats\nCecil provides some default formats, which can be overridden in the configuration file: html (default), atom, rss, json, xml, txt, amp, js, webmanifest, xsl, jsonfeed, iframe, oembed.\noutput.pagetypeformats\nIt’s not required to set output variable for each page, as Cecil automatically applies the formats defined for each page type (homepage, page, section, vocabulary and term).\noutput:\n  pagetypeformats:\n    page: [&lt;format&gt;]\n    homepage: [&lt;format&gt;]\n    section: [&lt;format&gt;]\n    vocabulary: [&lt;format&gt;]\n    term: [&lt;format&gt;]\nSeveral formats can be defined for the one type of page. For example the section page type can be automatically rendered in HTML and Atom:\noutput:\n  pagetypeformats:\n    section: [html, atom]\nTo render a page, Cecil lookup for a template named &lt;layout&gt;.&lt;format&gt;.twig (e.g. page.html.twig)\noutput example\noutput:\n  dir: _site\n  formats:\n    - name: html\n      mediatype: text\/html\n      filename: index\n      extension: html\n    - name: atom\n      mediatype: application\/xml\n      filename: atom\n      extension: xml\n      exclude: [redirect, paginated]\n  pagetypeformats:\n    page: [html]\n    homepage: [html, atom]\n    section: [html, atom]\n    vocabulary: [html]\n    term: [html, atom]\nPost process\nYou can extend Cecil capabilities with an Output post processor to modify the output files after they have been generated.\n",
      "content_html": "<h1>Output</h1>\n<p>Defines where and in what format pages are rendered.</p>\n<h2 id=\"output-dir\">output.dir</h2>\n<p>Directory where rendered pages’ files are saved (<code translate=\"no\">_site</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">output:</span>\n  <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">_site</span></code></pre>\n<h2 id=\"output-formats\">output.formats</h2>\n<p>List of output formats definition, which are used to render pages (e.g. HTML, Atom, RSS, JSON, XML, etc.).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">output:</span>\n  <span class=\"hljs-attr\">formats:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">&lt;name&gt;</span>            <span class=\"hljs-comment\"># name of the format, e.g.: `html` (required)</span>\n      <span class=\"hljs-attr\">mediatype:</span> <span class=\"hljs-string\">&lt;media</span> <span class=\"hljs-string\">type&gt;</span> <span class=\"hljs-comment\"># media type (MIME type), ie: 'text/html' (optional)</span>\n      <span class=\"hljs-attr\">subpath:</span> <span class=\"hljs-string\">&lt;sub</span> <span class=\"hljs-string\">path&gt;</span>     <span class=\"hljs-comment\"># sub path, e.g.: `amp` in `path/amp/index.html` (optional)</span>\n      <span class=\"hljs-attr\">filename:</span> <span class=\"hljs-string\">&lt;file</span> <span class=\"hljs-string\">name&gt;</span>   <span class=\"hljs-comment\"># file name, e.g.: `index` in `path/index.html` (optional)</span>\n      <span class=\"hljs-attr\">extension:</span> <span class=\"hljs-string\">&lt;extension&gt;</span>  <span class=\"hljs-comment\"># file extension, e.g.: `html` in `path/index.html` (required)</span>\n      <span class=\"hljs-attr\">exclude:</span> <span class=\"hljs-string\">[&lt;variable&gt;]</span>   <span class=\"hljs-comment\"># don’t apply this format to pages identified by listed variables, e.g.: `[redirect, paginated]` (optional)</span></code></pre>\n<p>Those formats are used in the <a href=\"#output-pagetypeformats\"><code translate=\"no\">output.pagetypeformats</code></a> configuration and in the <a href=\"../content/2-front-matter.md#output\"><code translate=\"no\">output</code> page variable</a>.</p>\n<h3 id=\"default-formats\">Default formats</h3>\n<p>Cecil provides some <a href=\"https://github.com/Cecilapp/Cecil/blob/main/config/base.php#L81-L162\" target=\"_blank\" rel=\"noopener noreferrer\">default formats</a>, which can be overridden in the configuration file: <code translate=\"no\">html</code> (default), <code translate=\"no\">atom</code>, <code translate=\"no\">rss</code>, <code translate=\"no\">json</code>, <code translate=\"no\">xml</code>, <code translate=\"no\">txt</code>, <code translate=\"no\">amp</code>, <code translate=\"no\">js</code>, <code translate=\"no\">webmanifest</code>, <code translate=\"no\">xsl</code>, <code translate=\"no\">jsonfeed</code>, <code translate=\"no\">iframe</code>, <code translate=\"no\">oembed</code>.</p>\n<h2 id=\"output-pagetypeformats\">output.pagetypeformats</h2>\n<p>It’s not required to set <code translate=\"no\">output</code> variable for each page, as Cecil automatically applies the formats defined for each page type (<code translate=\"no\">homepage</code>, <code translate=\"no\">page</code>, <code translate=\"no\">section</code>, <code translate=\"no\">vocabulary</code> and <code translate=\"no\">term</code>).</p>\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\">page:</span> <span class=\"hljs-string\">[&lt;format&gt;]</span>\n    <span class=\"hljs-attr\">homepage:</span> <span class=\"hljs-string\">[&lt;format&gt;]</span>\n    <span class=\"hljs-attr\">section:</span> <span class=\"hljs-string\">[&lt;format&gt;]</span>\n    <span class=\"hljs-attr\">vocabulary:</span> <span class=\"hljs-string\">[&lt;format&gt;]</span>\n    <span class=\"hljs-attr\">term:</span> <span class=\"hljs-string\">[&lt;format&gt;]</span></code></pre>\n<p>Several formats can be defined for the one type of page. For example the <code translate=\"no\">section</code> page type can be automatically rendered in HTML and Atom:</p>\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\">section:</span> <span class=\"hljs-string\">[html,</span> <span class=\"hljs-string\">atom]</span></code></pre>\n<aside class=\"note note-info\"><p>To render a page, <a href=\"../templates/1-lookup-rules.md#lookup-rules\">Cecil lookup for a template</a> named <code translate=\"no\">&lt;layout&gt;.&lt;format&gt;.twig</code> (e.g. <code translate=\"no\">page.html.twig</code>)</p></aside>\n<h2 id=\"output-example\">output example</h2>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">output:</span>\n  <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">_site</span>\n  <span class=\"hljs-attr\">formats:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">html</span>\n      <span class=\"hljs-attr\">mediatype:</span> <span class=\"hljs-string\">text/html</span>\n      <span class=\"hljs-attr\">filename:</span> <span class=\"hljs-string\">index</span>\n      <span class=\"hljs-attr\">extension:</span> <span class=\"hljs-string\">html</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">atom</span>\n      <span class=\"hljs-attr\">mediatype:</span> <span class=\"hljs-string\">application/xml</span>\n      <span class=\"hljs-attr\">filename:</span> <span class=\"hljs-string\">atom</span>\n      <span class=\"hljs-attr\">extension:</span> <span class=\"hljs-string\">xml</span>\n      <span class=\"hljs-attr\">exclude:</span> <span class=\"hljs-string\">[redirect,</span> <span class=\"hljs-string\">paginated]</span>\n  <span class=\"hljs-attr\">pagetypeformats:</span>\n    <span class=\"hljs-attr\">page:</span> <span class=\"hljs-string\">[html]</span>\n    <span class=\"hljs-attr\">homepage:</span> <span class=\"hljs-string\">[html,</span> <span class=\"hljs-string\">atom]</span>\n    <span class=\"hljs-attr\">section:</span> <span class=\"hljs-string\">[html,</span> <span class=\"hljs-string\">atom]</span>\n    <span class=\"hljs-attr\">vocabulary:</span> <span class=\"hljs-string\">[html]</span>\n    <span class=\"hljs-attr\">term:</span> <span class=\"hljs-string\">[html,</span> <span class=\"hljs-string\">atom]</span></code></pre>\n<h2 id=\"post-process\">Post process</h2>\n<p>You can extend Cecil capabilities with an <a href=\"../developers/1-extend.md#output-post-processor\">Output post processor</a> to modify the output files after they have been generated.</p>\n<hr>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/cache/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/cache/",
      "title": "Cache",
      "summary": "Assets, templates and translations cache.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Cache\nCache options.\ncache.enabled\nCache is enabled by default (true), but you can disable it with:\ncache:\n  enabled: false\nIt’s not recommended to disable the cache for performance reasons.\ncache.dir\nDirectory where cache files are stored (.cache by default).\ncache:\n  dir: '.cache'\nThe cache directory is relative to the site directory, but you can use an absolute path: it can be useful to store the cache in a shared directory.\ncache.assets\nAssets cache options.\ncache.assets.ttl\nTime to live of assets cache in seconds (null by default = no expiration).\ncache:\n  assets:\n    ttl: ~\ncache.assets.remote.ttl\nTime to live of remote assets cache in seconds (7 days by default).\ncache:\n  assets:\n    remotes:\n      ttl: 604800 # 7 days\ncache.templates\nDisables templates cache with false (true by default).\ncache:\n  templates: true\nSee templates cache documentation for more details.\ncache.translations\nDisables translations cache  with false (true by default).\ncache:\n  translations: true\n",
      "content_html": "<h1>Cache</h1>\n<p>Cache options.</p>\n<h2 id=\"cache-enabled\">cache.enabled</h2>\n<p>Cache is enabled by default (<code translate=\"no\">true</code>), but you can disable it with:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">cache:</span>\n  <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">false</span></code></pre>\n<aside class=\"note note-warning\"><p>It’s not recommended to disable the cache for performance reasons.</p></aside>\n<h2 id=\"cache-dir\">cache.dir</h2>\n<p>Directory where cache files are stored (<code translate=\"no\">.cache</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">cache:</span>\n  <span class=\"hljs-attr\">dir:</span> <span class=\"hljs-string\">'.cache'</span></code></pre>\n<aside class=\"note note-info\"><p>The cache directory is relative to the site directory, but you can use an absolute path: it can be useful to store the cache in a shared directory.</p></aside>\n<h2 id=\"cache-assets\">cache.assets</h2>\n<p>Assets cache options.</p>\n<h3 id=\"cache-assets-ttl\">cache.assets.ttl</h3>\n<p>Time to live of assets cache in seconds (<code translate=\"no\">null</code> by default = no expiration).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">cache:</span>\n  <span class=\"hljs-attr\">assets:</span>\n    <span class=\"hljs-attr\">ttl:</span> <span class=\"hljs-string\">~</span></code></pre>\n<h3 id=\"cache-assets-remote-ttl\">cache.assets.remote.ttl</h3>\n<p>Time to live of remote assets cache in seconds (7 days by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">cache:</span>\n  <span class=\"hljs-attr\">assets:</span>\n    <span class=\"hljs-attr\">remotes:</span>\n      <span class=\"hljs-attr\">ttl:</span> <span class=\"hljs-number\">604800</span> <span class=\"hljs-comment\"># 7 days</span></code></pre>\n<h2 id=\"cache-templates\">cache.templates</h2>\n<p>Disables templates cache with <code translate=\"no\">false</code> (<code translate=\"no\">true</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">cache:</span>\n  <span class=\"hljs-attr\">templates:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<aside class=\"note note-info\"><p>See <a href=\"../templates/6-cache.md\">templates cache documentation</a> for more details.</p></aside>\n<h2 id=\"cache-translations\">cache.translations</h2>\n<p>Disables translations cache  with <code translate=\"no\">false</code> (<code translate=\"no\">true</code> by default).</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">cache:</span>\n  <span class=\"hljs-attr\">translations:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<hr>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/server/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/server/",
      "title": "Server and optimization",
      "summary": "Local server headers and output optimization.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Server and optimization\nThese options configure the local preview server and the optimization of the generated files.\nServer\nserver.headers\nYou can define custom HTTP headers, used by the local preview server.\nSince version 8.38.0, the headers option has been moved to the server.headers section.\nserver:\n  headers:\n    - path: &lt;path&gt; # Relative path, prefixed with a slash. Support \"*\" wildcard.\n      headers:\n        - key: &lt;key&gt;\n          value: \"&lt;value&gt;\"\nIt's useful to test custom Content Security Policy or Cache-Control.\nExample:\nserver:\n  headers:\n    - path: \/*\n      headers:\n        - key: X-Frame-Options\n          value: \"SAMEORIGIN\"\n        - key: X-XSS-Protection\n          value: \"1; mode=block\"\n        - key: X-Content-Type-Options\n          value: \"nosniff\"\n        - key: Content-Security-Policy\n          value: \"default-src 'self'; object-src 'self'; img-src 'self'\"\n        - key: Strict-Transport-Security\n          value: \"max-age=31536000; includeSubDomains; preload\"\n    - path: \/assets\/*\n      headers:\n        - key: Cache-Control\n          value: \"public, max-age=31536000\"\n    - path: \/foo.html\n      headers:\n        - key: Foo\n          value: \"bar\"\n\nOptimize\nThe optimization options allow to enable compression of output files: HTML, CSS, JavaScript and image.\noptimize:\n  enabled: false     # enables files optimization (`false` by default)\n  html:\n    enabled: true    # enables HTML files optimization\n    ext: [html, htm]   # supported files extensions\n  css:\n    enabled: true    # enables CSS files optimization\n    ext: [css]         # supported files extensions\n  js:\n    enabled: true    # enables JavaScript files optimization\n    ext: [js]          # supported files extensions\n  images:\n    enabled: true    # enables images files optimization\n    ext: [jpeg, jpg, png, gif, webp, svg, avif] # supported files extensions\nThis option is disabled by default and can be enabled via:\noptimize: true\nOnce the global option is enabled, the 4 file types will be processed.\nIt is possible to disable each of them via enabled: false and modify processed files extension via ext.\nIt is also possible to enable this option through CLI when using the \"build\" and \"serve\" commands via the --optimize option.\nImages compressor will use these binaries if they are present in the system: JpegOptim, Optipng, Pngquant 2, SVGO, Gifsicle, cwebp and avifenc.\n",
      "content_html": "<h1>Server and optimization</h1>\n<p>These options configure the local preview server and the optimization of the generated files.</p>\n<h2 id=\"server\">Server</h2>\n<h3 id=\"server-headers\">server.headers</h3>\n<p>You can define custom <a href=\"https://developer.mozilla.org/docs/Glossary/Response_header\" target=\"_blank\" rel=\"noopener noreferrer\">HTTP headers</a>, used by the local preview server.</p>\n<aside class=\"note note-warning\"><p>Since version <ins>8.38.0</ins>, the <code translate=\"no\">headers</code> option has been moved to the <code translate=\"no\">server.headers</code> section.</p></aside>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">server:</span>\n  <span class=\"hljs-attr\">headers:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">&lt;path&gt;</span> <span class=\"hljs-comment\"># Relative path, prefixed with a slash. Support \"*\" wildcard.</span>\n      <span class=\"hljs-attr\">headers:</span>\n        <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">&lt;key&gt;</span>\n          <span class=\"hljs-attr\">value:</span> <span class=\"hljs-string\">\"&lt;value&gt;\"</span></code></pre>\n<aside class=\"note note-tip\"><p>It's useful to test custom <a href=\"https://developer.mozilla.org/docs/Web/HTTP/CSP\" target=\"_blank\" rel=\"noopener noreferrer\">Content Security Policy</a> or <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control\" target=\"_blank\" rel=\"noopener noreferrer\">Cache-Control</a>.</p></aside>\n<p><em>Example:</em></p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">server:</span>\n  <span class=\"hljs-attr\">headers:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">/*</span>\n      <span class=\"hljs-attr\">headers:</span>\n        <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">X-Frame-Options</span>\n          <span class=\"hljs-attr\">value:</span> <span class=\"hljs-string\">\"SAMEORIGIN\"</span>\n        <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">X-XSS-Protection</span>\n          <span class=\"hljs-attr\">value:</span> <span class=\"hljs-string\">\"1; mode=block\"</span>\n        <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">X-Content-Type-Options</span>\n          <span class=\"hljs-attr\">value:</span> <span class=\"hljs-string\">\"nosniff\"</span>\n        <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">Content-Security-Policy</span>\n          <span class=\"hljs-attr\">value:</span> <span class=\"hljs-string\">\"default-src 'self'; object-src 'self'; img-src 'self'\"</span>\n        <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">Strict-Transport-Security</span>\n          <span class=\"hljs-attr\">value:</span> <span class=\"hljs-string\">\"max-age=31536000; includeSubDomains; preload\"</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">/assets/*</span>\n      <span class=\"hljs-attr\">headers:</span>\n        <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">Cache-Control</span>\n          <span class=\"hljs-attr\">value:</span> <span class=\"hljs-string\">\"public, max-age=31536000\"</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">/foo.html</span>\n      <span class=\"hljs-attr\">headers:</span>\n        <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">Foo</span>\n          <span class=\"hljs-attr\">value:</span> <span class=\"hljs-string\">\"bar\"</span></code></pre>\n<hr>\n<h2 id=\"optimize\">Optimize</h2>\n<p>The optimization options allow to enable compression of output files: HTML, CSS, JavaScript and image.</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">optimize:</span>\n  <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">false</span>     <span class=\"hljs-comment\"># enables files optimization (`false` by default)</span>\n  <span class=\"hljs-attr\">html:</span>\n    <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>    <span class=\"hljs-comment\"># enables HTML files optimization</span>\n    <span class=\"hljs-attr\">ext:</span> <span class=\"hljs-string\">[html,</span> <span class=\"hljs-string\">htm]</span>   <span class=\"hljs-comment\"># supported files extensions</span>\n  <span class=\"hljs-attr\">css:</span>\n    <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>    <span class=\"hljs-comment\"># enables CSS files optimization</span>\n    <span class=\"hljs-attr\">ext:</span> <span class=\"hljs-string\">[css]</span>         <span class=\"hljs-comment\"># supported files extensions</span>\n  <span class=\"hljs-attr\">js:</span>\n    <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>    <span class=\"hljs-comment\"># enables JavaScript files optimization</span>\n    <span class=\"hljs-attr\">ext:</span> <span class=\"hljs-string\">[js]</span>          <span class=\"hljs-comment\"># supported files extensions</span>\n  <span class=\"hljs-attr\">images:</span>\n    <span class=\"hljs-attr\">enabled:</span> <span class=\"hljs-literal\">true</span>    <span class=\"hljs-comment\"># enables images files optimization</span>\n    <span class=\"hljs-attr\">ext:</span> <span class=\"hljs-string\">[jpeg,</span> <span class=\"hljs-string\">jpg,</span> <span class=\"hljs-string\">png,</span> <span class=\"hljs-string\">gif,</span> <span class=\"hljs-string\">webp,</span> <span class=\"hljs-string\">svg,</span> <span class=\"hljs-string\">avif]</span> <span class=\"hljs-comment\"># supported files extensions</span></code></pre>\n<p>This option is disabled by default and can be enabled via:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">optimize:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<p>Once the global option is enabled, the 4 file types will be processed.<br>\nIt is possible to disable each of them via <code translate=\"no\">enabled: false</code> and modify processed files extension via <code translate=\"no\">ext</code>.</p>\n<aside class=\"note note-tip\"><p>It is also possible to enable this option through CLI when using the \"build\" and \"serve\" commands via the <code translate=\"no\">--optimize</code> option.</p></aside>\n<aside class=\"note note-important\"><p><strong>Images</strong> compressor will use these binaries if they are present in the system: <a href=\"https://github.com/tjko/jpegoptim\" target=\"_blank\" rel=\"noopener noreferrer\">JpegOptim</a>, <a href=\"http://optipng.sourceforge.net/\" target=\"_blank\" rel=\"noopener noreferrer\">Optipng</a>, <a href=\"https://pngquant.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Pngquant 2</a>, <a href=\"https://github.com/svg/svgo\" target=\"_blank\" rel=\"noopener noreferrer\">SVGO</a>, <a href=\"http://www.lcdf.org/gifsicle/\" target=\"_blank\" rel=\"noopener noreferrer\">Gifsicle</a>, <a href=\"https://developers.google.com/speed/webp/docs/cwebp\" target=\"_blank\" rel=\"noopener noreferrer\">cwebp</a> and <a href=\"https://github.com/AOMediaCodec/libavif\" target=\"_blank\" rel=\"noopener noreferrer\">avifenc</a>.</p></aside>\n<hr>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/override/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/override/",
      "title": "Override configuration",
      "summary": "Override the configuration with environment variables or a CLI option.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Override configuration\nThe configuration can be overridden without editing the configuration file, through environment variables or a CLI option.\nEnvironment variables\nThe configuration can be overridden through environment variables.\nAt startup, Cecil also attempts to load a .env file from the current site path (the current working directory, or the &lt;path&gt; argument if provided).\n\nIf the .env file does not exist, Cecil continues normally.\nVariables already defined by the shell\/system are preserved.\n\nEach environment variable name must be prefixed with CECIL_ and the configuration key must be set in uppercase.\nFor example, the following command set the website’s baseurl:\nexport CECIL_BASEURL=\"https:\/\/example.com\/\"\nYou can store the same value in a .env file at your project root:\nCECIL_BASEURL=\"https:\/\/example.com\/\"\nCECIL_TITLE=\"My Cecil site\"\nCLI option\nYou can combine multiple configuration files, with the --config option (left-to-right precedence):\nphp cecil.phar --config config-1.yml,config-2.yml",
      "content_html": "<h1>Override configuration</h1>\n<p>The configuration can be overridden without editing the configuration file, through environment variables or a CLI option.</p>\n<h2 id=\"environment-variables\">Environment variables</h2>\n<p>The configuration can be overridden through <a href=\"https://en.wikipedia.org/wiki/Environment_variable\" target=\"_blank\" rel=\"noopener noreferrer\">environment variables</a>.</p>\n<p>At startup, Cecil also attempts to load a <code translate=\"no\">.env</code> file from the current site path (the current working directory, or the <code translate=\"no\">&lt;path&gt;</code> argument if provided).</p>\n<ul>\n<li>If the <code translate=\"no\">.env</code> file does not exist, Cecil continues normally.</li>\n<li>Variables already defined by the shell/system are preserved.</li>\n</ul>\n<p>Each environment variable name must be prefixed with <code translate=\"no\">CECIL_</code> and the configuration key must be set in uppercase.</p>\n<p>For example, the following command set the website’s <code translate=\"no\">baseurl</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://example.com/\"</span></code></pre>\n<p>You can store the same value in a <code translate=\"no\">.env</code> file at your project root:</p>\n<pre><code class=\"language-dotenv\" translate=\"no\">CECIL_BASEURL=\"https://example.com/\"\nCECIL_TITLE=\"My Cecil site\"</code></pre>\n<h2 id=\"cli-option\">CLI option</h2>\n<p>You can combine multiple configuration files, with the <code translate=\"no\">--config</code> option (left-to-right precedence):</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar --config config-1.yml,config-2.yml</code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/commands/new-site/",
      "url": "https://cecil-945.pages.dev/documentation/commands/new-site/",
      "title": "new:site",
      "summary": "Create a new website.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "new:site\nCreates a new site.\nUsage\nDescription:\n  Creates a new website\n\nUsage:\n  new:site [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                  Use the given path as working directory\n\nOptions:\n  -f, --force           Override directory if it already exists\n      --demo            Add demo content (pages, templates and assets)\n  -h, --help            Display help for the given command. When no command is given display help for the list command\n  -q, --quiet           Do not output any message\n  -V, --version         Display this application version\n      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction  Do not ask any interactive question\n  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The new:site command creates a new website in the current directory, or in &lt;path&gt; if provided.\n  If you run this command without any options, it will ask you for the website title, baseline, base URL, description, etc.\n\n    cecil.phar new:site\n    cecil.phar new:site path\/to\/the\/working\/directory\n\n  To create a new website with demo content, run:\n\n    cecil.phar new:site --demo\n\n  To override an existing website, run:\n\n    cecil.phar new:site --force",
      "content_html": "<h1>new:site</h1>\n<p>Creates a new site.</p>\n<h2 id=\"usage\">Usage</h2>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Creates a new website\n\nUsage:\n  new:site [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                  Use the given path as working directory\n\nOptions:\n  -f, --force           Override directory if it already exists\n      --demo            Add demo content (pages, templates and assets)\n  -h, --help            Display help for the given command. When no command is given display help for the list command\n  -q, --quiet           Do not output any message\n  -V, --version         Display this application version\n      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction  Do not ask any interactive question\n  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The new:site command creates a new website in the current directory, or in &lt;path&gt; if provided.\n  If you run this command without any options, it will ask you for the website title, baseline, base URL, description, etc.\n\n    cecil.phar new:site\n    cecil.phar new:site path/to/the/working/directory\n\n  To create a new website with demo content, run:\n\n    cecil.phar new:site --demo\n\n  To override an existing website, run:\n\n    cecil.phar new:site --force</code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/deploy/jamstack-platforms/",
      "url": "https://cecil-945.pages.dev/documentation/deploy/jamstack-platforms/",
      "title": "Jamstack platforms",
      "summary": "Netlify, Vercel, statichost, Cloudflare Pages, Render.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-14T00:00:00+00:00","content_text": "Jamstack platforms\nJamstack platforms can build and deploy your Cecil site automatically on each push to your Git repository.\nNetlify\n\nA powerful serverless platform with an intuitive git-based workflow. Automated deployments, shareable previews, and much more.\n\n➡️ https:\/\/www.netlify.com\nnetlify.toml:\n[build]\n  publish = \"_site\"\n  command = \"curl -sSOL https:\/\/cecil.app\/build.sh &amp;&amp; bash .\/build.sh\"\n[context.production.environment]\n  CECIL_ENV = \"production\"\n[context.deploy-preview.environment]\n  CECIL_ENV = \"preview\"\nOfficial documentation\nVercel\n\nVercel combines the best developer experience with an obsessive focus on end-user performance.\n\n➡️ https:\/\/vercel.com\nvercel.json:\n{\n  \"buildCommand\": \"curl -sSOL https:\/\/cecil.app\/build.sh &amp;&amp; bash .\/build.sh\",\n  \"outputDirectory\": \"_site\"\n}\nOfficial documentation\nstatichost\n\nModern static site hosting with European servers and absolutely no personal data collection!\n\n➡️ https:\/\/statichost.eu\nstatichost.yml:\nimage: wordpress:cli-php8.4\ncommand: curl -sSOL https:\/\/cecil.app\/build.sh &amp;&amp; bash .\/build.sh\npublic: _site\nOfficial documentation\nCloudflare Pages\nCloudflare Pages doesn’t provide PHP: the build script uses the Cecil standalone binary instead, and installs themes from composer.lock.\n\nCloudflare Pages is a JAMstack platform for frontend developers to collaborate and deploy websites.\n\n➡️ https:\/\/pages.cloudflare.com\nBuild configurations:\n\nFramework preset: None\nBuild command: curl -sSOL https:\/\/cecil.app\/build.sh &amp;&amp; bash .\/build.sh\nBuild output directory: _site\n\nOfficial documentation\nRender\nRender only provides PHP 7.3: the build script uses the Cecil standalone binary instead, and installs themes from composer.lock.\n\nRender is a unified cloud to build and run all your apps and websites with free TLS certificates, global CDN, private networks and auto deploys from Git.\n\n➡️ https:\/\/render.com\nrender.yaml:\npreviewsEnabled: true\nservices:\n  - type: web\n    name: Cecil\n    env: static\n    buildCommand: curl -sSOL https:\/\/cecil.app\/build.sh &amp;&amp; bash .\/build.sh\n    staticPublishPath: _site\n    pullRequestPreviewsEnabled: true\nOfficial documentation",
      "content_html": "<h1>Jamstack platforms</h1>\n<p>Jamstack platforms can build and deploy your Cecil site automatically on each push to your Git repository.</p>\n<h2 id=\"netlify\">Netlify</h2>\n<blockquote>\n<p>A powerful serverless platform with an intuitive git-based workflow. Automated deployments, shareable previews, and much more.</p>\n</blockquote>\n<p>➡️ <a href=\"https://www.netlify.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.netlify.com</a></p>\n<p><em>netlify.toml</em>:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">[build]\n  publish = <span class=\"hljs-string\">\"_site\"</span>\n  <span class=\"hljs-built_in\">command</span> = <span class=\"hljs-string\">\"curl -sSOL https://cecil.app/build.sh &amp;&amp; bash ./build.sh\"</span>\n[context.production.environment]\n  CECIL_ENV = <span class=\"hljs-string\">\"production\"</span>\n[context.deploy-preview.environment]\n  CECIL_ENV = <span class=\"hljs-string\">\"preview\"</span></code></pre>\n<p><a href=\"https://www.netlify.com/docs/continuous-deployment/\" target=\"_blank\" rel=\"noopener noreferrer\">Official documentation</a></p>\n<h2 id=\"vercel\">Vercel</h2>\n<blockquote>\n<p>Vercel combines the best developer experience with an obsessive focus on end-user performance.</p>\n</blockquote>\n<p>➡️ <a href=\"https://vercel.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://vercel.com</a></p>\n<p><em>vercel.json</em>:</p>\n<pre><code class=\"language-json hljs json\" translate=\"no\">{\n  <span class=\"hljs-attr\">\"buildCommand\"</span>: <span class=\"hljs-string\">\"curl -sSOL https://cecil.app/build.sh &amp;&amp; bash ./build.sh\"</span>,\n  <span class=\"hljs-attr\">\"outputDirectory\"</span>: <span class=\"hljs-string\">\"_site\"</span>\n}</code></pre>\n<p><a href=\"https://vercel.com/docs/concepts/deployments/build-step#build-command\" target=\"_blank\" rel=\"noopener noreferrer\">Official documentation</a></p>\n<h2 id=\"statichost\">statichost</h2>\n<blockquote>\n<p>Modern static site hosting with European servers and absolutely no personal data collection!</p>\n</blockquote>\n<p>➡️ <a href=\"https://statichost.eu\" target=\"_blank\" rel=\"noopener noreferrer\">https://statichost.eu</a></p>\n<p><em>statichost.yml</em>:</p>\n<pre><code class=\"language-yml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">image:</span> <span class=\"hljs-string\">wordpress:cli-php8.4</span>\n<span class=\"hljs-attr\">command:</span> <span class=\"hljs-string\">curl</span> <span class=\"hljs-string\">-sSOL</span> <span class=\"hljs-string\">https://cecil.app/build.sh</span> <span class=\"hljs-string\">&amp;&amp;</span> <span class=\"hljs-string\">bash</span> <span class=\"hljs-string\">./build.sh</span>\n<span class=\"hljs-attr\">public:</span> <span class=\"hljs-string\">_site</span></code></pre>\n<p><a href=\"https://www.statichost.eu/docs/\" target=\"_blank\" rel=\"noopener noreferrer\">Official documentation</a></p>\n<h2 id=\"cloudflare-pages\">Cloudflare Pages</h2>\n<aside class=\"note note-info\"><p>Cloudflare Pages doesn’t provide PHP: the build script uses the Cecil <a href=\"../getting-started/2-installation.md#standalone-binary\">standalone binary</a> instead, and installs themes from <code translate=\"no\">composer.lock</code>.</p></aside>\n<blockquote>\n<p>Cloudflare Pages is a JAMstack platform for frontend developers to collaborate and deploy websites.</p>\n</blockquote>\n<p>➡️ <a href=\"https://pages.cloudflare.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://pages.cloudflare.com</a></p>\n<p>Build configurations:</p>\n<ul>\n<li>Framework preset: <code translate=\"no\">None</code></li>\n<li>Build command: <code translate=\"no\">curl -sSOL https://cecil.app/build.sh &amp;&amp; bash ./build.sh</code></li>\n<li>Build output directory: <code translate=\"no\">_site</code></li>\n</ul>\n<p><a href=\"https://developers.cloudflare.com/pages/\" target=\"_blank\" rel=\"noopener noreferrer\">Official documentation</a></p>\n<h2 id=\"render\">Render</h2>\n<aside class=\"note note-info\"><p>Render only provides PHP 7.3: the build script uses the Cecil <a href=\"../getting-started/2-installation.md#standalone-binary\">standalone binary</a> instead, and installs themes from <code translate=\"no\">composer.lock</code>.</p></aside>\n<blockquote>\n<p>Render is a unified cloud to build and run all your apps and websites with free TLS certificates, global CDN, private networks and auto deploys from Git.</p>\n</blockquote>\n<p>➡️ <a href=\"https://render.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://render.com</a></p>\n<p><em>render.yaml</em>:</p>\n<pre><code class=\"language-yml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">previewsEnabled:</span> <span class=\"hljs-literal\">true</span>\n<span class=\"hljs-attr\">services:</span>\n  <span class=\"hljs-bullet\">-</span> <span class=\"hljs-attr\">type:</span> <span class=\"hljs-string\">web</span>\n    <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Cecil</span>\n    <span class=\"hljs-attr\">env:</span> <span class=\"hljs-string\">static</span>\n    <span class=\"hljs-attr\">buildCommand:</span> <span class=\"hljs-string\">curl</span> <span class=\"hljs-string\">-sSOL</span> <span class=\"hljs-string\">https://cecil.app/build.sh</span> <span class=\"hljs-string\">&amp;&amp;</span> <span class=\"hljs-string\">bash</span> <span class=\"hljs-string\">./build.sh</span>\n    <span class=\"hljs-attr\">staticPublishPath:</span> <span class=\"hljs-string\">_site</span>\n    <span class=\"hljs-attr\">pullRequestPreviewsEnabled:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<p><a href=\"https://render.com/docs/static-sites\" target=\"_blank\" rel=\"noopener noreferrer\">Official documentation</a></p>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/getting-started/quick-start/",
      "url": "https://cecil-945.pages.dev/documentation/getting-started/quick-start/",
      "title": "Quick Start",
      "summary": "Install Cecil, create a new website, add pages, preview it locally, then build and deploy it.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Quick Start\nThis guide walks you through creating your first Cecil website, previewing it locally, then building and deploying it.\nCreate a website\nYou can create a new website from scratch in a few minutes.\nFollow the steps below to create your first Cecil website.\n\n\n\n\n\n\n\n\nDemo of the expected result: https:\/\/cecilapp.github.io\/skeleton\/.\nPrerequisites\n\nPHP 8.3+\nTerminal (a basic understanding of terminal)\nText editor, like VS Code and\/or Typora\n\n1. Download Cecil\nDownload cecil.phar from your terminal:\ncurl -LO https:\/\/cecil.app\/cecil.phar\nYou can also download Cecil manually, or use:\n\nHomebrew: brew install cecilapp\/tap\/cecil\nScoop: scoop install https:\/\/cecil.app\/scoop\/cecil.json\n\n2. Create a new site\nCreate a directory for the website (e.g.: &lt;mywebsite&gt;), put cecil.phar in it, then run the new:site command:\nphp cecil.phar new:site\n3. Add a page\nRun the new:page command:\nphp cecil.phar new:page\nNow you can edit the newly created page with your Markdown editor: &lt;mywebsite&gt;\/pages\/&lt;new-page&gt;.md.\nWe recommend you to use Typora to edit your Markdown files.\n4. Check the preview\nRun the following command to create a preview of the website:\nphp cecil.phar serve\nThen navigate to http:\/\/localhost:8000.\nThe serve command runs a local HTTP server and a watcher: if a file (a page, a template or the config) is modified, the browser’s current page is automatically reloaded.\n5. Build and deploy\nWhen you are satisfied with the result, you can generate the website in order to deploy it on the Web.\nRun the following command to build the website:\nphp cecil.phar build\nYou can now copy the content of the _site directory to a Web server 🎉\n",
      "content_html": "<h1>Quick Start</h1>\n<p>This guide walks you through creating your first Cecil website, previewing it locally, then building and deploying it.</p>\n<h2 id=\"create-a-website\">Create a website</h2>\n<p>You can create a new website from scratch in a few minutes.</p>\n<p>Follow the steps below to create your first Cecil website.</p>\n<p><a href=\"https://cecilapp.github.io/skeleton/\" target=\"_blank\" rel=\"noopener noreferrer\"><picture>\n<source media=\"(prefers-color-scheme: dark)\" type=\"image/avif\" srcset=\"/thumbnails/480x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.avif 480w, /thumbnails/768x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.avif 768w, /docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.avif 1007w\" sizes=\"100vw\">\n<source media=\"(prefers-color-scheme: dark)\" type=\"image/webp\" srcset=\"/thumbnails/480x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.webp 480w, /thumbnails/768x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.webp 768w, /docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.webp 1007w\" sizes=\"100vw\">\n<source media=\"(prefers-color-scheme: dark)\" srcset=\"/thumbnails/480x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.png 480w, /thumbnails/768x/docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.png 768w, /docs/cecil-newsite.dark.19f0a851a99b34775f44b67c805a0815.png 1007w\" sizes=\"100vw\">\n<source type=\"image/avif\" srcset=\"/thumbnails/480x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.avif 480w, /thumbnails/768x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.avif 768w, /docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.avif 1007w\" width=\"1007\" height=\"607\" sizes=\"100vw\">\n<source type=\"image/webp\" srcset=\"/thumbnails/480x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.webp 480w, /thumbnails/768x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.webp 768w, /docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.webp 1007w\" width=\"1007\" height=\"607\" sizes=\"100vw\">\n<img src=\"/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.png\" alt=\"New website example\" loading=\"lazy\" decoding=\"async\" class=\"dark:brightness-90\" width=\"1007\" height=\"607\" style=\";max-width:100%;height:auto;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAyCAYAAACqNX6+AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEj0lEQVR4nN1b0ZKDIAxMHP//H+9HLvcg0QQCRATE2xmLrQghy4ZIW/z5+SEAgN/fXyAio6SjpKPk4wQRUCgBIJyDBgIgIiAibNvmOBA23AA3hA3xaKJSJh02IX/f0Q0Cd1ezxzTLgb3tNgAAShxvkjEYZPbnMcLyGBmORHW9nWwfmglRjmCVvALZ8x1n2ffFBCOOJ0HigUJAhyl10g85d6yrjOATYYodVm08IwTGkVF3wf9SBuMRIQQI5wDF6Uj8V2UwDELYs5h8alb1MCGyrMMD9bmP0Zvrs/+pDIYghPM5AiA8/ZwlGVE4CMuTCUN9cXiJOZtIkzr4ijLIkHVOPftVIZhBePKRdzDKOaYKu354iUipEiO45lcUrz6sqYyYJCZol0YgXOI4P1B3HRV0kPIRghlCcsSo/I1iQ54oo+T095TB2PV11OZsod/zwEtJkWEehcin2SIpFK8WlEi27javMqoNdUVOGYxLIUEZtn0IiAREmozoKSSLM8yEUGcSgSjCU2h3mjLuYIwyGJdC1NqBR4/hOBZUUgvr3YjL7cpSkSKmw7kFg3DYkMpSt6vwTWUwToUcixqAzJgQECgoA4EuH4nSAzxLvEq8Sukdq4+zb7Kv614kvqMMxs6TTxPBbgC+AgSoHAFgu6AEuQLw7ql1tYaYjHjF+aIyGPrBEMMsRIQrCzzmZ0xGK1TaWsytU+gJkp7LmvjYUt2rTxliyjV2v8MlEWWABCKGsNVpd0Tk1WmK7c+fUpWkdfyt9kWrMg+FKE+jLjhuI3VRyNULyjfFlq1JUFPJczvnKoMhHgz18njGaQ5jYg3phgdKqakkp5zReLpmqb0snqg6oyG5tdV9gCWl1MgvqaTdTqkMzkCjGpR6oleykN9+P0c4YY4VlZJHSSVzd6b6ZXN7rIxrmlEyorEDFMnrjY5yKjFDmYdktWbE18Ypg5EqZKYyrL49nwmUyFCrI97Osl3o/Zyzs4HUJwAPWvTv3WI/OJZRVobRz6D5einEkUJ1dXYJDYOtpcZJ8w8dOmoHIHlSf+r0aaQ5cfopyCZeIlVgeFEZjKYfOazmdImibXSpozVCj94byxLS6vSVyQKAbAxTa88LymBUFVJy8MrOrymlJAvr0qxdY3fI8jh/ZYJyUNkYGXtik7P/hJA4bYxLy+mrEvHULit8TVvUvRuHJWLA2cYKqO0Wy3ozoRRSU4c1CDmQFcnoZdMsYvaWLfUvEmNhJWUwdu7c2gfyqkSer0hGj7VkFm6vIVb9rxDjwVvKYDStId4t7y8R8zYRjO2JIXjz/E1Q5lgNyRrSgppKViHlC7j9YHgnfK2IVe1iNC/qMXJqWNUBKI6VsFtMtKjEun9lxISsQsxeCjke50t8IWRJWISgccxE8svFpwbw/SsrxMoCS0qZ/mCYCzEeQ+I6UxXSoZMcIUlIm/0FVfWvd07IZrqppLMzSip4e01xfUFlkZUz+P531Q1D7+itXFaY62L49yGef/jwr7zvhLCyOpyjGjj4rMPHdemCUyH3zfyCOp40P+w79ZKzrf/M9cP7Knmxqyz+AEm28LzQnWd/AAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center;background-size:cover;\" srcset=\"/thumbnails/480x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.png 480w, /thumbnails/768x/docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.png 768w, /docs/cecil-newsite.94982e8ace758b40e6ff753807bdb42b.png 1007w\" sizes=\"100vw\">\n</picture></a></p>\n<aside class=\"note note-info\"><p>Demo of the expected result: <a href=\"https://cecilapp.github.io/skeleton/\" target=\"_blank\" rel=\"noopener noreferrer\">https://cecilapp.github.io/skeleton/</a>.</p></aside>\n<h3 id=\"prerequisites\">Prerequisites</h3>\n<ul>\n<li><a href=\"https://php.net/manual/en/install.php\" target=\"_blank\" rel=\"noopener noreferrer\">PHP</a> 8.3+</li>\n<li>Terminal (a basic understanding of <a href=\"https://wikipedia.org/wiki/Terminal_emulator\" target=\"_blank\" rel=\"noopener noreferrer\">terminal</a>)</li>\n<li>Text editor, like <a href=\"https://code.visualstudio.com\" target=\"_blank\" rel=\"noopener noreferrer\">VS Code</a> and/or <a href=\"https://typora.io\" target=\"_blank\" rel=\"noopener noreferrer\">Typora</a></li>\n</ul>\n<h3 id=\"1-download-cecil\">1. Download Cecil</h3>\n<p>Download <code translate=\"no\">cecil.phar</code> from your terminal:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">curl -LO https://cecil.app/cecil.phar</code></pre>\n<p>You can also <a href=\"https://cecil.app/download/\" target=\"_blank\" rel=\"noopener noreferrer\">download Cecil manually</a>, or use:</p>\n<ul>\n<li><a href=\"https://brew.sh\" target=\"_blank\" rel=\"noopener noreferrer\">Homebrew</a>: <code translate=\"no\">brew install cecilapp/tap/cecil</code></li>\n<li><a href=\"https://scoop.sh\" target=\"_blank\" rel=\"noopener noreferrer\">Scoop</a>: <code translate=\"no\">scoop install https://cecil.app/scoop/cecil.json</code></li>\n</ul>\n<h3 id=\"2-create-a-new-site\">2. Create a new site</h3>\n<p>Create a directory for the website (e.g.: <code translate=\"no\">&lt;mywebsite&gt;</code>), put <code translate=\"no\">cecil.phar</code> in it, then run the <code translate=\"no\">new:site</code> command:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar new:site</code></pre>\n<h3 id=\"3-add-a-page\">3. Add a page</h3>\n<p>Run the <code translate=\"no\">new:page</code> command:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar new:page</code></pre>\n<p>Now you can edit the newly created page with your Markdown editor: <code translate=\"no\">&lt;mywebsite&gt;/pages/&lt;new-page&gt;.md</code>.</p>\n<aside class=\"note note-tip\"><p>We recommend you to use <a href=\"https://www.typora.io\" target=\"_blank\" rel=\"noopener noreferrer\">Typora</a> to edit your Markdown files.</p></aside>\n<h3 id=\"4-check-the-preview\">4. Check the preview</h3>\n<p>Run the following command to create a preview of the website:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar serve</code></pre>\n<p>Then navigate to <code translate=\"no\">http://localhost:8000</code>.</p>\n<aside class=\"note note-info\"><p>The <code translate=\"no\">serve</code> command runs a local HTTP server and a watcher: if a file (a page, a template or the config) is modified, the browser’s current page is automatically reloaded.</p></aside>\n<h3 id=\"5-build-and-deploy\">5. Build and deploy</h3>\n<p>When you are satisfied with the result, you can generate the website in order to deploy it on the Web.</p>\n<p>Run the following command to build the website:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar build</code></pre>\n<p>You can now copy the content of the <code translate=\"no\">_site</code> directory to a Web server 🎉</p>\n<hr>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/commands/new-page/",
      "url": "https://cecil-945.pages.dev/documentation/commands/new-page/",
      "title": "new:page",
      "summary": "Create a new page, optionally from a model.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "new:page\nCreates a new page.\nUsage\nDescription:\n  Creates a new page\n\nUsage:\n  new:page [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                        Use the given path as working directory\n\nOptions:\n      --name=NAME             Page path name\n      --slugify|--no-slugify  Slugify file name (or disable --no-slugify)\n  -p, --prefix                Prefix the file name with the current date (`YYYY-MM-DD`)\n  -f, --force                 Override the file if already exist\n  -o, --open                  Open editor automatically\n      --editor=EDITOR         Editor to use with open option\n  -h, --help                  Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                 Do not output any message\n  -V, --version               Display this application version\n      --ansi|--no-ansi        Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction        Do not ask any interactive question\n  -v|vv|vvv, --verbose        Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The new:page command creates a new page file.\n  If you run this command without any options, it will ask you for the page name and other options.\n\n    cecil.phar new:page\n    cecil.phar new:page --name=path\/to\/a-page.md\n    cecil.phar new:page --name=path\/to\/A Page.md --slugify\n\n  To create a new page with a date prefix (i.e: `YYYY-MM-DD`), run:\n\n    cecil.phar new:page --prefix\n\n  To create a new page and open it with an editor, run:\n\n    cecil.phar new:page --open --editor=editor\n\n  To override an existing page, run:\n\n    cecil.phar new:page --force\nPage’s models\nYou can define your own models for your new pages in the models directory:\n\nThe name must be based on the section’s name (e.g.: blog.md)\nThe default model must be named default.md (for root pages or pages’s section without model)\n\nTwo dynamic variables are available:\n\n%title%: the file’s name\n%date%: the current date\n\nOpen with your editor\nWith the --open option, the editor will be opened automatically. So use editor key in your configuration file to define the default editor (e.g.: editor: typora).",
      "content_html": "<h1>new:page</h1>\n<p>Creates a new page.</p>\n<h2 id=\"usage\">Usage</h2>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Creates a new page\n\nUsage:\n  new:page [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                        Use the given path as working directory\n\nOptions:\n      --name=NAME             Page path name\n      --slugify|--no-slugify  Slugify file name (or disable --no-slugify)\n  -p, --prefix                Prefix the file name with the current date (`YYYY-MM-DD`)\n  -f, --force                 Override the file if already exist\n  -o, --open                  Open editor automatically\n      --editor=EDITOR         Editor to use with open option\n  -h, --help                  Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                 Do not output any message\n  -V, --version               Display this application version\n      --ansi|--no-ansi        Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction        Do not ask any interactive question\n  -v|vv|vvv, --verbose        Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The new:page command creates a new page file.\n  If you run this command without any options, it will ask you for the page name and other options.\n\n    cecil.phar new:page\n    cecil.phar new:page --name=path/to/a-page.md\n    cecil.phar new:page --name=path/to/A Page.md --slugify\n\n  To create a new page with a date prefix (i.e: `YYYY-MM-DD`), run:\n\n    cecil.phar new:page --prefix\n\n  To create a new page and open it with an editor, run:\n\n    cecil.phar new:page --open --editor=editor\n\n  To override an existing page, run:\n\n    cecil.phar new:page --force</code></pre>\n<h2 id=\"page-s-models\">Page’s models</h2>\n<p>You can define your own models for your new pages in the <code translate=\"no\">models</code> directory:</p>\n<ol>\n<li>The name must be based on the section’s name (e.g.: <code translate=\"no\">blog.md</code>)</li>\n<li>The default model must be named <code translate=\"no\">default.md</code> (for root pages or pages’s section without model)</li>\n</ol>\n<p>Two dynamic variables are available:</p>\n<ol>\n<li><code translate=\"no\">%title%</code>: the file’s name</li>\n<li><code translate=\"no\">%date%</code>: the current date</li>\n</ol>\n<h2 id=\"open-with-your-editor\">Open with your editor</h2>\n<p>With the <code translate=\"no\">--open</code> option, the editor will be opened automatically. So use <code translate=\"no\">editor</code> key in your configuration file to define the default editor (e.g.: <code translate=\"no\">editor: typora</code>).</p>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/deploy/continuous-deployment/",
      "url": "https://cecil-945.pages.dev/documentation/deploy/continuous-deployment/",
      "title": "Continuous build & deploy",
      "summary": "GitHub Pages and GitLab CI.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Continuous build &amp; deploy\nContinuous integration services can build your site with Cecil and deploy it on each change to your repository.\nGitHub Pages\n\nWebsites for you and your projects, hosted directly from your GitHub repository. Just edit, push, and your changes are live.\n\n➡️ https:\/\/pages.github.com\n.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: Restore Cecil cache\n        uses: actions\/cache\/restore@v5\n        with:\n          path: .\/.cache\n          key: cecil-cache-\n          restore-keys: |\n            cecil-cache-\n      - name: Build site\n        uses: Cecilapp\/Cecil-Action@v4\n      - name: Save Cecil cache\n        uses: actions\/cache\/save@v5\n        with:\n          path: .\/.cache\n          key: cecil-cache-${{ hashFiles('.\/.cache\/**\/*') }}\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\nOfficial documentation\nGitLab CI\n\nWith GitLab Pages, you can publish static websites directly from a repository in GitLab.\n\n➡️ https:\/\/about.gitlab.com\/solutions\/continuous-integration\/\n.gitlab-ci.yml:\nimage: wordpress:cli-php8.4\ntest:\n  stage: test\n  variables:\n    CECIL_OUTPUT_DIR: test\n  script:\n    - curl -sSOL https:\/\/cecil.app\/build.sh &amp;&amp; bash .\/build.sh\n  artifacts:\n    paths:\n     - test\n  except:\n   - master\npages:\n  stage: deploy\n  variables:\n    CECIL_ENV: production\n    CECIL_OUTPUT_DIR: public\n  script:\n    - curl -sSOL https:\/\/cecil.app\/build.sh &amp;&amp; bash .\/build.sh\n  artifacts:\n    paths:\n      - public\n  only:\n    - master\ncache:\n  paths:\n    - composer-cache\/\n    - vendor\/\n    - .cache\/\nOfficial documentation",
      "content_html": "<h1>Continuous build &amp; deploy</h1>\n<p>Continuous integration services can build your site with Cecil and deploy it on each change to your repository.</p>\n<h2 id=\"github-pages\">GitHub Pages</h2>\n<blockquote>\n<p>Websites for you and your projects, hosted directly from your GitHub repository. Just edit, push, and your changes are live.</p>\n</blockquote>\n<p>➡️ <a href=\"https://pages.github.com\" target=\"_blank\" rel=\"noopener noreferrer\">https://pages.github.com</a></p>\n<p><em>.github/workflows/build-and-deploy.yml</em>:</p>\n<pre><code class=\"language-yml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Build</span> <span class=\"hljs-string\">and</span> <span class=\"hljs-string\">deploy</span> <span class=\"hljs-string\">to</span> <span class=\"hljs-string\">GitHub</span> <span class=\"hljs-string\">Pages</span>\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\">Restore</span> <span class=\"hljs-string\">Cecil</span> <span class=\"hljs-string\">cache</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/cache/restore@v5</span>\n        <span class=\"hljs-attr\">with:</span>\n          <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">./.cache</span>\n          <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">cecil-cache-</span>\n          <span class=\"hljs-attr\">restore-keys:</span> <span class=\"hljs-string\">|\n            cecil-cache-\n</span>      <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-bullet\">-</span> <span class=\"hljs-attr\">name:</span> <span class=\"hljs-string\">Save</span> <span class=\"hljs-string\">Cecil</span> <span class=\"hljs-string\">cache</span>\n        <span class=\"hljs-attr\">uses:</span> <span class=\"hljs-string\">actions/cache/save@v5</span>\n        <span class=\"hljs-attr\">with:</span>\n          <span class=\"hljs-attr\">path:</span> <span class=\"hljs-string\">./.cache</span>\n          <span class=\"hljs-attr\">key:</span> <span class=\"hljs-string\">cecil-cache-${{</span> <span class=\"hljs-string\">hashFiles('./.cache/**/*')</span> <span class=\"hljs-string\">}}</span>\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><a href=\"https://docs.github.com/en/pages\" target=\"_blank\" rel=\"noopener noreferrer\">Official documentation</a></p>\n<h2 id=\"gitlab-ci\">GitLab CI</h2>\n<blockquote>\n<p>With GitLab Pages, you can publish static websites directly from a repository in GitLab.</p>\n</blockquote>\n<p>➡️ <a href=\"https://about.gitlab.com/solutions/continuous-integration/\" target=\"_blank\" rel=\"noopener noreferrer\">https://about.gitlab.com/solutions/continuous-integration/</a></p>\n<p><em>.gitlab-ci.yml</em>:</p>\n<pre><code class=\"language-yml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">image:</span> <span class=\"hljs-string\">wordpress:cli-php8.4</span>\n<span class=\"hljs-attr\">test:</span>\n  <span class=\"hljs-attr\">stage:</span> <span class=\"hljs-string\">test</span>\n  <span class=\"hljs-attr\">variables:</span>\n    <span class=\"hljs-attr\">CECIL_OUTPUT_DIR:</span> <span class=\"hljs-string\">test</span>\n  <span class=\"hljs-attr\">script:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">curl</span> <span class=\"hljs-string\">-sSOL</span> <span class=\"hljs-string\">https://cecil.app/build.sh</span> <span class=\"hljs-string\">&amp;&amp;</span> <span class=\"hljs-string\">bash</span> <span class=\"hljs-string\">./build.sh</span>\n  <span class=\"hljs-attr\">artifacts:</span>\n    <span class=\"hljs-attr\">paths:</span>\n     <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">test</span>\n  <span class=\"hljs-attr\">except:</span>\n   <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">master</span>\n<span class=\"hljs-attr\">pages:</span>\n  <span class=\"hljs-attr\">stage:</span> <span class=\"hljs-string\">deploy</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>\n  <span class=\"hljs-attr\">script:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">curl</span> <span class=\"hljs-string\">-sSOL</span> <span class=\"hljs-string\">https://cecil.app/build.sh</span> <span class=\"hljs-string\">&amp;&amp;</span> <span class=\"hljs-string\">bash</span> <span class=\"hljs-string\">./build.sh</span>\n  <span class=\"hljs-attr\">artifacts:</span>\n    <span class=\"hljs-attr\">paths:</span>\n      <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">public</span>\n  <span class=\"hljs-attr\">only:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">master</span>\n<span class=\"hljs-attr\">cache:</span>\n  <span class=\"hljs-attr\">paths:</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">composer-cache/</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">vendor/</span>\n    <span class=\"hljs-bullet\">-</span> <span class=\"hljs-string\">.cache/</span></code></pre>\n<p><a href=\"https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/\" target=\"_blank\" rel=\"noopener noreferrer\">Official documentation</a></p>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/commands/serve/",
      "url": "https://cecil-945.pages.dev/documentation/commands/serve/",
      "title": "serve",
      "summary": "Preview the website with the built-in server.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "serve\nBuilds and serves the site locally.\nThe web server is designed to aid website testing. It is not intended to be a full-featured web server and it should not be used on a public network.\nUsage\nDescription:\n  Starts the built-in server\n\nUsage:\n  serve [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                             Use the given path as working directory\n\nOptions:\n  -o, --open                       Open web browser automatically\n      --host=HOST                  Server host [default: \"localhost\"]\n      --port=PORT                  Server port [default: \"8000\"]\n  -w, --watch|--no-watch           Enable (or disable --no-watch) changes watcher (enabled by default)\n  -i, --incremental                Enable incremental builds (rebuild only changed pages)\n  -d, --drafts                     Include drafts\n      --optimize|--no-optimize     Enable (or disable --no-optimize) optimization of generated files\n  -c, --config=CONFIG              Set the path to extra config files (comma-separated)\n      --clear-cache[=CLEAR-CACHE]  Clear cache before build (optional cache key as regular expression) [default: false]\n  -p, --page=PAGE                  Build a specific page\n      --no-ignore-vcs              Changes watcher must not ignore VCS directories\n  -m, --metrics                    Show build metrics (duration and memory) of each step\n      --timeout=TIMEOUT            Sets the process timeout (max. runtime) in seconds [default: 7200]\n      --notify                     Send desktop notification on server start\n  -b, --background                 Run the server in the background\n  -h, --help                       Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                      Do not output any message\n  -V, --version                    Display this application version\n      --ansi|--no-ansi             Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction             Do not ask any interactive question\n  -v|vv|vvv, --verbose             Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The serve command starts the live-reloading-built-in web server.\n\n    cecil.phar serve\n    cecil.phar serve path\/to\/the\/working\/directory\n    cecil.phar serve --open\n    cecil.phar serve --drafts\n    cecil.phar serve --no-watch\n\n  To speed up local development you can enable incremental builds with the --incremental option.\n  When content pages change, Cecil rebuilds just those pages.\n  When templates change, Cecil rebuilds only pages using those templates (including Twig dependencies such as extends\/include).\n  Any other change (data, config, static or asset file, or file deletion) triggers a full rebuild:\n\n    cecil.phar serve --incremental\n\n  You can use a custom host and port by using the --host and --port options:\n\n    cecil.phar serve --host=127.0.0.1 --port=8080\n\n  To build the website with an extra configuration file, you can use the --config option.\n  This is useful during local development to override some settings without modifying the main configuration:\n\n    cecil.phar serve --config=config\/dev.yml\n\n  To start the server with changes watcher not ignoring VCS directories, run:\n\n    cecil.phar serve --no-ignore-vcs\n\n  To define the process timeout (in seconds), run:\n\n    cecil.phar serve --timeout=7200\n\n  To run the server in the background, run:\n\n    cecil.phar serve --background\n    cecil.phar serve -b\n\n  Then stop it with:\n\n    cecil.phar serve:stop\n\n  In background mode, file changes are not watched automatically.\nserve:background\nAlias of serve --background: starts the built-in server in the background without occupying the terminal.\nDescription:\n  Starts the built-in server in the background (alias of `serve --background`)\n\nUsage:\n  serve:background [options] [--] [&lt;path&gt;]\n\nHelp:\n  The serve:background command starts the built-in web server in the background.\n\n    cecil.phar serve:background\n    cecil.phar serve:background path\/to\/the\/working\/directory\n\n  This command is an alias of serve --background.\n\n  Stop the server with:\n\n    cecil.phar serve:stop\nserve:log\nDisplays the combined server and error logs.\nDescription:\n  Shows combined server and error logs\n\nUsage:\n  serve:log [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                  Use the given path as working directory\n\nOptions:\n  -l, --lines=LINES     Number of entries to display (default: 25)\n\nHelp:\n  The serve:log command displays entries from combined server and error logs, sorted by date.\n\n    cecil.phar serve:log\n    cecil.phar serve:log path\/to\/the\/working\/directory\n    cecil.phar serve:log --lines=100\n    cecil.phar serve:log -l 100\n\n  This command shows logs from `.cecil\/errors.log` and `.cecil\/server.log`.\n  It is useful for debugging issues with your local development server.",
      "content_html": "<h1>serve</h1>\n<p>Builds and serves the site locally.</p>\n<aside class=\"note note-warning\"><p>The web server is designed to aid website testing. It is not intended to be a full-featured web server and it should not be used on a public network.</p></aside>\n<h2 id=\"usage\">Usage</h2>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Starts the built-in server\n\nUsage:\n  serve [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                             Use the given path as working directory\n\nOptions:\n  -o, --open                       Open web browser automatically\n      --host=HOST                  Server host [default: \"localhost\"]\n      --port=PORT                  Server port [default: \"8000\"]\n  -w, --watch|--no-watch           Enable (or disable --no-watch) changes watcher (enabled by default)\n  -i, --incremental                Enable incremental builds (rebuild only changed pages)\n  -d, --drafts                     Include drafts\n      --optimize|--no-optimize     Enable (or disable --no-optimize) optimization of generated files\n  -c, --config=CONFIG              Set the path to extra config files (comma-separated)\n      --clear-cache[=CLEAR-CACHE]  Clear cache before build (optional cache key as regular expression) [default: false]\n  -p, --page=PAGE                  Build a specific page\n      --no-ignore-vcs              Changes watcher must not ignore VCS directories\n  -m, --metrics                    Show build metrics (duration and memory) of each step\n      --timeout=TIMEOUT            Sets the process timeout (max. runtime) in seconds [default: 7200]\n      --notify                     Send desktop notification on server start\n  -b, --background                 Run the server in the background\n  -h, --help                       Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                      Do not output any message\n  -V, --version                    Display this application version\n      --ansi|--no-ansi             Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction             Do not ask any interactive question\n  -v|vv|vvv, --verbose             Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The serve command starts the live-reloading-built-in web server.\n\n    cecil.phar serve\n    cecil.phar serve path/to/the/working/directory\n    cecil.phar serve --open\n    cecil.phar serve --drafts\n    cecil.phar serve --no-watch\n\n  To speed up local development you can enable incremental builds with the --incremental option.\n  When content pages change, Cecil rebuilds just those pages.\n  When templates change, Cecil rebuilds only pages using those templates (including Twig dependencies such as extends/include).\n  Any other change (data, config, static or asset file, or file deletion) triggers a full rebuild:\n\n    cecil.phar serve --incremental\n\n  You can use a custom host and port by using the --host and --port options:\n\n    cecil.phar serve --host=127.0.0.1 --port=8080\n\n  To build the website with an extra configuration file, you can use the --config option.\n  This is useful during local development to override some settings without modifying the main configuration:\n\n    cecil.phar serve --config=config/dev.yml\n\n  To start the server with changes watcher not ignoring VCS directories, run:\n\n    cecil.phar serve --no-ignore-vcs\n\n  To define the process timeout (in seconds), run:\n\n    cecil.phar serve --timeout=7200\n\n  To run the server in the background, run:\n\n    cecil.phar serve --background\n    cecil.phar serve -b\n\n  Then stop it with:\n\n    cecil.phar serve:stop\n\n  In background mode, file changes are not watched automatically.</code></pre>\n<h2 id=\"serve-background\">serve:background</h2>\n<p>Alias of <code translate=\"no\">serve --background</code>: starts the built-in server in the background without occupying the terminal.</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Starts the built-in server in the background (alias of `serve --background`)\n\nUsage:\n  serve:background [options] [--] [&lt;path&gt;]\n\nHelp:\n  The serve:background command starts the built-in web server in the background.\n\n    cecil.phar serve:background\n    cecil.phar serve:background path/to/the/working/directory\n\n  This command is an alias of serve --background.\n\n  Stop the server with:\n\n    cecil.phar serve:stop</code></pre>\n<h2 id=\"serve-log\">serve:log</h2>\n<p>Displays the combined server and error logs.</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Shows combined server and error logs\n\nUsage:\n  serve:log [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                  Use the given path as working directory\n\nOptions:\n  -l, --lines=LINES     Number of entries to display (default: 25)\n\nHelp:\n  The serve:log command displays entries from combined server and error logs, sorted by date.\n\n    cecil.phar serve:log\n    cecil.phar serve:log path/to/the/working/directory\n    cecil.phar serve:log --lines=100\n    cecil.phar serve:log -l 100\n\n  This command shows logs from `.cecil/errors.log` and `.cecil/server.log`.\n  It is useful for debugging issues with your local development server.</code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/configuration/locale-codes/",
      "url": "https://cecil-945.pages.dev/documentation/configuration/locale-codes/",
      "title": "Locale codes",
      "summary": "List of available locale codes to use with the languages option.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Locale codes\nAvailable locale codes (language_COUNTRY) used by the languages option.\nList of locale codes\n\n\n\nLocale\nCode\n\n\n\n\nAlbanian (Albania)\nsq_AL\n\n\nAlbanian\nsq\n\n\nArabic (Algeria)\nar_DZ\n\n\nArabic (Bahrain)\nar_BH\n\n\nArabic (Egypt)\nar_EG\n\n\nArabic (Iraq)\nar_IQ\n\n\nArabic (Jordan)\nar_JO\n\n\nArabic (Kuwait)\nar_KW\n\n\nArabic (Lebanon)\nar_LB\n\n\nArabic (Libya)\nar_LY\n\n\nArabic (Morocco)\nar_MA\n\n\nArabic (Oman)\nar_OM\n\n\nArabic (Qatar)\nar_QA\n\n\nArabic (Saudi Arabia)\nar_SA\n\n\nArabic (Sudan)\nar_SD\n\n\nArabic (Syria)\nar_SY\n\n\nArabic (Tunisia)\nar_TN\n\n\nArabic (United Arab Emirates)\nar_AE\n\n\nArabic (Yemen)\nar_YE\n\n\nArabic\nar\n\n\nBelarusian (Belarus)\nbe_BY\n\n\nBelarusian\nbe\n\n\nBulgarian (Bulgaria)\nbg_BG\n\n\nBulgarian\nbg\n\n\nCatalan (Spain)\nca_ES\n\n\nCatalan\nca\n\n\nChinese (China)\nzh_CN\n\n\nChinese (Hong Kong)\nzh_HK\n\n\nChinese (Singapore)\nzh_SG\n\n\nChinese (Taiwan)\nzh_TW\n\n\nChinese\nzh\n\n\nCroatian (Croatia)\nhr_HR\n\n\nCroatian\nhr\n\n\nCzech (Czech Republic)\ncs_CZ\n\n\nCzech\ncs\n\n\nDanish (Denmark)\nda_DK\n\n\nDanish\nda\n\n\nDutch (Belgium)\nnl_BE\n\n\nDutch (Netherlands)\nnl_NL\n\n\nDutch\nnl\n\n\nEnglish (Australia)\nen_AU\n\n\nEnglish (Canada)\nen_CA\n\n\nEnglish (India)\nen_IN\n\n\nEnglish (Ireland)\nen_IE\n\n\nEnglish (Malta)\nen_MT\n\n\nEnglish (New Zealand)\nen_NZ\n\n\nEnglish (Philippines)\nen_PH\n\n\nEnglish (Singapore)\nen_SG\n\n\nEnglish (South Africa)\nen_ZA\n\n\nEnglish (United Kingdom)\nen_GB\n\n\nEnglish (United States)\nen_US\n\n\nEnglish\nen\n\n\nEstonian (Estonia)\net_EE\n\n\nEstonian\net\n\n\nFinnish (Finland)\nfi_FI\n\n\nFinnish\nfi\n\n\nFrench (Belgium)\nfr_BE\n\n\nFrench (Canada)\nfr_CA\n\n\nFrench (France)\nfr_FR\n\n\nFrench (Luxembourg)\nfr_LU\n\n\nFrench (Switzerland)\nfr_CH\n\n\nFrench\nfr\n\n\nGerman (Austria)\nde_AT\n\n\nGerman (Germany)\nde_DE\n\n\nGerman (Luxembourg)\nde_LU\n\n\nGerman (Switzerland)\nde_CH\n\n\nGerman\nde\n\n\nGreek (Cyprus)\nel_CY\n\n\nGreek (Greece)\nel_GR\n\n\nGreek\nel\n\n\nHebrew (Israel)\niw_IL\n\n\nHebrew\niw\n\n\nHindi (India)\nhi_IN\n\n\nHungarian (Hungary)\nhu_HU\n\n\nHungarian\nhu\n\n\nIcelandic (Iceland)\nis_IS\n\n\nIcelandic\nis\n\n\nIndonesian (Indonesia)\nin_ID\n\n\nIndonesian\nin\n\n\nIrish (Ireland)\nga_IE\n\n\nIrish\nga\n\n\nItalian (Italy)\nit_IT\n\n\nItalian (Switzerland)\nit_CH\n\n\nItalian\nit\n\n\nJapanese (Japan)\nja_JP\n\n\nJapanese (Japan,JP)\nja_JP_JP\n\n\nJapanese\nja\n\n\nKorean (South Korea)\nko_KR\n\n\nKorean\nko\n\n\nLatvian (Latvia)\nlv_LV\n\n\nLatvian\nlv\n\n\nLithuanian (Lithuania)\nlt_LT\n\n\nLithuanian\nlt\n\n\nMacedonian (Macedonia)\nmk_MK\n\n\nMacedonian\nmk\n\n\nMalay (Malaysia)\nms_MY\n\n\nMalay\nms\n\n\nMaltese (Malta)\nmt_MT\n\n\nMaltese\nmt\n\n\nNorwegian (Norway)\nno_NO\n\n\nNorwegian (Norway,Nynorsk)\nno_NO_NY\n\n\nNorwegian\nno\n\n\nPolish (Poland)\npl_PL\n\n\nPolish\npl\n\n\nPortuguese (Brazil)\npt_BR\n\n\nPortuguese (Portugal)\npt_PT\n\n\nPortuguese\npt\n\n\nRomanian (Romania)\nro_RO\n\n\nRomanian\nro\n\n\nRussian (Russia)\nru_RU\n\n\nRussian\nru\n\n\nSerbian (Bosnia and Herzegovina)\nsr_BA\n\n\nSerbian (Montenegro)\nsr_ME\n\n\nSerbian (Serbia and Montenegro)\nsr_CS\n\n\nSerbian (Serbia)\nsr_RS\n\n\nSerbian\nsr\n\n\nSlovak (Slovakia)\nsk_SK\n\n\nSlovak\nsk\n\n\nSlovenian (Slovenia)\nsl_SI\n\n\nSlovenian\nsl\n\n\nSpanish (Argentina)\nes_AR\n\n\nSpanish (Bolivia)\nes_BO\n\n\nSpanish (Chile)\nes_CL\n\n\nSpanish (Colombia)\nes_CO\n\n\nSpanish (Costa Rica)\nes_CR\n\n\nSpanish (Dominican Republic)\nes_DO\n\n\nSpanish (Ecuador)\nes_EC\n\n\nSpanish (El Salvador)\nes_SV\n\n\nSpanish (Guatemala)\nes_GT\n\n\nSpanish (Honduras)\nes_HN\n\n\nSpanish (Mexico)\nes_MX\n\n\nSpanish (Nicaragua)\nes_NI\n\n\nSpanish (Panama)\nes_PA\n\n\nSpanish (Paraguay)\nes_PY\n\n\nSpanish (Peru)\nes_PE\n\n\nSpanish (Puerto Rico)\nes_PR\n\n\nSpanish (Spain)\nes_ES\n\n\nSpanish (United States)\nes_US\n\n\nSpanish (Uruguay)\nes_UY\n\n\nSpanish (Venezuela)\nes_VE\n\n\nSpanish\nes\n\n\nSwedish (Sweden)\nsv_SE\n\n\nSwedish\nsv\n\n\nThai (Thailand)\nth_TH\n\n\nThai (Thailand,TH)\nth_TH_TH\n\n\nThai\nth\n\n\nTurkish (Turkey)\ntr_TR\n\n\nTurkish\ntr\n\n\nUkrainian (Ukraine)\nuk_UA\n\n\nUkrainian\nuk\n\n\nVietnamese (Vietnam)\nvi_VN\n\n\nVietnamese\nvi\n\n\n",
      "content_html": "<h1>Locale codes</h1>\n<p>Available locale codes (<code translate=\"no\">language_COUNTRY</code>) used by the <a href=\"2-languages.md#languages\"><code translate=\"no\">languages</code></a> option.</p>\n<h2 id=\"list-of-locale-codes\">List of locale codes</h2>\n<table>\n<thead>\n<tr>\n<th>Locale</th>\n<th>Code</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Albanian (Albania)</td>\n<td>sq_AL</td>\n</tr>\n<tr>\n<td>Albanian</td>\n<td>sq</td>\n</tr>\n<tr>\n<td>Arabic (Algeria)</td>\n<td>ar_DZ</td>\n</tr>\n<tr>\n<td>Arabic (Bahrain)</td>\n<td>ar_BH</td>\n</tr>\n<tr>\n<td>Arabic (Egypt)</td>\n<td>ar_EG</td>\n</tr>\n<tr>\n<td>Arabic (Iraq)</td>\n<td>ar_IQ</td>\n</tr>\n<tr>\n<td>Arabic (Jordan)</td>\n<td>ar_JO</td>\n</tr>\n<tr>\n<td>Arabic (Kuwait)</td>\n<td>ar_KW</td>\n</tr>\n<tr>\n<td>Arabic (Lebanon)</td>\n<td>ar_LB</td>\n</tr>\n<tr>\n<td>Arabic (Libya)</td>\n<td>ar_LY</td>\n</tr>\n<tr>\n<td>Arabic (Morocco)</td>\n<td>ar_MA</td>\n</tr>\n<tr>\n<td>Arabic (Oman)</td>\n<td>ar_OM</td>\n</tr>\n<tr>\n<td>Arabic (Qatar)</td>\n<td>ar_QA</td>\n</tr>\n<tr>\n<td>Arabic (Saudi Arabia)</td>\n<td>ar_SA</td>\n</tr>\n<tr>\n<td>Arabic (Sudan)</td>\n<td>ar_SD</td>\n</tr>\n<tr>\n<td>Arabic (Syria)</td>\n<td>ar_SY</td>\n</tr>\n<tr>\n<td>Arabic (Tunisia)</td>\n<td>ar_TN</td>\n</tr>\n<tr>\n<td>Arabic (United Arab Emirates)</td>\n<td>ar_AE</td>\n</tr>\n<tr>\n<td>Arabic (Yemen)</td>\n<td>ar_YE</td>\n</tr>\n<tr>\n<td>Arabic</td>\n<td>ar</td>\n</tr>\n<tr>\n<td>Belarusian (Belarus)</td>\n<td>be_BY</td>\n</tr>\n<tr>\n<td>Belarusian</td>\n<td>be</td>\n</tr>\n<tr>\n<td>Bulgarian (Bulgaria)</td>\n<td>bg_BG</td>\n</tr>\n<tr>\n<td>Bulgarian</td>\n<td>bg</td>\n</tr>\n<tr>\n<td>Catalan (Spain)</td>\n<td>ca_ES</td>\n</tr>\n<tr>\n<td>Catalan</td>\n<td>ca</td>\n</tr>\n<tr>\n<td>Chinese (China)</td>\n<td>zh_CN</td>\n</tr>\n<tr>\n<td>Chinese (Hong Kong)</td>\n<td>zh_HK</td>\n</tr>\n<tr>\n<td>Chinese (Singapore)</td>\n<td>zh_SG</td>\n</tr>\n<tr>\n<td>Chinese (Taiwan)</td>\n<td>zh_TW</td>\n</tr>\n<tr>\n<td>Chinese</td>\n<td>zh</td>\n</tr>\n<tr>\n<td>Croatian (Croatia)</td>\n<td>hr_HR</td>\n</tr>\n<tr>\n<td>Croatian</td>\n<td>hr</td>\n</tr>\n<tr>\n<td>Czech (Czech Republic)</td>\n<td>cs_CZ</td>\n</tr>\n<tr>\n<td>Czech</td>\n<td>cs</td>\n</tr>\n<tr>\n<td>Danish (Denmark)</td>\n<td>da_DK</td>\n</tr>\n<tr>\n<td>Danish</td>\n<td>da</td>\n</tr>\n<tr>\n<td>Dutch (Belgium)</td>\n<td>nl_BE</td>\n</tr>\n<tr>\n<td>Dutch (Netherlands)</td>\n<td>nl_NL</td>\n</tr>\n<tr>\n<td>Dutch</td>\n<td>nl</td>\n</tr>\n<tr>\n<td>English (Australia)</td>\n<td>en_AU</td>\n</tr>\n<tr>\n<td>English (Canada)</td>\n<td>en_CA</td>\n</tr>\n<tr>\n<td>English (India)</td>\n<td>en_IN</td>\n</tr>\n<tr>\n<td>English (Ireland)</td>\n<td>en_IE</td>\n</tr>\n<tr>\n<td>English (Malta)</td>\n<td>en_MT</td>\n</tr>\n<tr>\n<td>English (New Zealand)</td>\n<td>en_NZ</td>\n</tr>\n<tr>\n<td>English (Philippines)</td>\n<td>en_PH</td>\n</tr>\n<tr>\n<td>English (Singapore)</td>\n<td>en_SG</td>\n</tr>\n<tr>\n<td>English (South Africa)</td>\n<td>en_ZA</td>\n</tr>\n<tr>\n<td>English (United Kingdom)</td>\n<td>en_GB</td>\n</tr>\n<tr>\n<td>English (United States)</td>\n<td>en_US</td>\n</tr>\n<tr>\n<td>English</td>\n<td>en</td>\n</tr>\n<tr>\n<td>Estonian (Estonia)</td>\n<td>et_EE</td>\n</tr>\n<tr>\n<td>Estonian</td>\n<td>et</td>\n</tr>\n<tr>\n<td>Finnish (Finland)</td>\n<td>fi_FI</td>\n</tr>\n<tr>\n<td>Finnish</td>\n<td>fi</td>\n</tr>\n<tr>\n<td>French (Belgium)</td>\n<td>fr_BE</td>\n</tr>\n<tr>\n<td>French (Canada)</td>\n<td>fr_CA</td>\n</tr>\n<tr>\n<td>French (France)</td>\n<td>fr_FR</td>\n</tr>\n<tr>\n<td>French (Luxembourg)</td>\n<td>fr_LU</td>\n</tr>\n<tr>\n<td>French (Switzerland)</td>\n<td>fr_CH</td>\n</tr>\n<tr>\n<td>French</td>\n<td>fr</td>\n</tr>\n<tr>\n<td>German (Austria)</td>\n<td>de_AT</td>\n</tr>\n<tr>\n<td>German (Germany)</td>\n<td>de_DE</td>\n</tr>\n<tr>\n<td>German (Luxembourg)</td>\n<td>de_LU</td>\n</tr>\n<tr>\n<td>German (Switzerland)</td>\n<td>de_CH</td>\n</tr>\n<tr>\n<td>German</td>\n<td>de</td>\n</tr>\n<tr>\n<td>Greek (Cyprus)</td>\n<td>el_CY</td>\n</tr>\n<tr>\n<td>Greek (Greece)</td>\n<td>el_GR</td>\n</tr>\n<tr>\n<td>Greek</td>\n<td>el</td>\n</tr>\n<tr>\n<td>Hebrew (Israel)</td>\n<td>iw_IL</td>\n</tr>\n<tr>\n<td>Hebrew</td>\n<td>iw</td>\n</tr>\n<tr>\n<td>Hindi (India)</td>\n<td>hi_IN</td>\n</tr>\n<tr>\n<td>Hungarian (Hungary)</td>\n<td>hu_HU</td>\n</tr>\n<tr>\n<td>Hungarian</td>\n<td>hu</td>\n</tr>\n<tr>\n<td>Icelandic (Iceland)</td>\n<td>is_IS</td>\n</tr>\n<tr>\n<td>Icelandic</td>\n<td>is</td>\n</tr>\n<tr>\n<td>Indonesian (Indonesia)</td>\n<td>in_ID</td>\n</tr>\n<tr>\n<td>Indonesian</td>\n<td>in</td>\n</tr>\n<tr>\n<td>Irish (Ireland)</td>\n<td>ga_IE</td>\n</tr>\n<tr>\n<td>Irish</td>\n<td>ga</td>\n</tr>\n<tr>\n<td>Italian (Italy)</td>\n<td>it_IT</td>\n</tr>\n<tr>\n<td>Italian (Switzerland)</td>\n<td>it_CH</td>\n</tr>\n<tr>\n<td>Italian</td>\n<td>it</td>\n</tr>\n<tr>\n<td>Japanese (Japan)</td>\n<td>ja_JP</td>\n</tr>\n<tr>\n<td>Japanese (Japan,JP)</td>\n<td>ja_JP_JP</td>\n</tr>\n<tr>\n<td>Japanese</td>\n<td>ja</td>\n</tr>\n<tr>\n<td>Korean (South Korea)</td>\n<td>ko_KR</td>\n</tr>\n<tr>\n<td>Korean</td>\n<td>ko</td>\n</tr>\n<tr>\n<td>Latvian (Latvia)</td>\n<td>lv_LV</td>\n</tr>\n<tr>\n<td>Latvian</td>\n<td>lv</td>\n</tr>\n<tr>\n<td>Lithuanian (Lithuania)</td>\n<td>lt_LT</td>\n</tr>\n<tr>\n<td>Lithuanian</td>\n<td>lt</td>\n</tr>\n<tr>\n<td>Macedonian (Macedonia)</td>\n<td>mk_MK</td>\n</tr>\n<tr>\n<td>Macedonian</td>\n<td>mk</td>\n</tr>\n<tr>\n<td>Malay (Malaysia)</td>\n<td>ms_MY</td>\n</tr>\n<tr>\n<td>Malay</td>\n<td>ms</td>\n</tr>\n<tr>\n<td>Maltese (Malta)</td>\n<td>mt_MT</td>\n</tr>\n<tr>\n<td>Maltese</td>\n<td>mt</td>\n</tr>\n<tr>\n<td>Norwegian (Norway)</td>\n<td>no_NO</td>\n</tr>\n<tr>\n<td>Norwegian (Norway,Nynorsk)</td>\n<td>no_NO_NY</td>\n</tr>\n<tr>\n<td>Norwegian</td>\n<td>no</td>\n</tr>\n<tr>\n<td>Polish (Poland)</td>\n<td>pl_PL</td>\n</tr>\n<tr>\n<td>Polish</td>\n<td>pl</td>\n</tr>\n<tr>\n<td>Portuguese (Brazil)</td>\n<td>pt_BR</td>\n</tr>\n<tr>\n<td>Portuguese (Portugal)</td>\n<td>pt_PT</td>\n</tr>\n<tr>\n<td>Portuguese</td>\n<td>pt</td>\n</tr>\n<tr>\n<td>Romanian (Romania)</td>\n<td>ro_RO</td>\n</tr>\n<tr>\n<td>Romanian</td>\n<td>ro</td>\n</tr>\n<tr>\n<td>Russian (Russia)</td>\n<td>ru_RU</td>\n</tr>\n<tr>\n<td>Russian</td>\n<td>ru</td>\n</tr>\n<tr>\n<td>Serbian (Bosnia and Herzegovina)</td>\n<td>sr_BA</td>\n</tr>\n<tr>\n<td>Serbian (Montenegro)</td>\n<td>sr_ME</td>\n</tr>\n<tr>\n<td>Serbian (Serbia and Montenegro)</td>\n<td>sr_CS</td>\n</tr>\n<tr>\n<td>Serbian (Serbia)</td>\n<td>sr_RS</td>\n</tr>\n<tr>\n<td>Serbian</td>\n<td>sr</td>\n</tr>\n<tr>\n<td>Slovak (Slovakia)</td>\n<td>sk_SK</td>\n</tr>\n<tr>\n<td>Slovak</td>\n<td>sk</td>\n</tr>\n<tr>\n<td>Slovenian (Slovenia)</td>\n<td>sl_SI</td>\n</tr>\n<tr>\n<td>Slovenian</td>\n<td>sl</td>\n</tr>\n<tr>\n<td>Spanish (Argentina)</td>\n<td>es_AR</td>\n</tr>\n<tr>\n<td>Spanish (Bolivia)</td>\n<td>es_BO</td>\n</tr>\n<tr>\n<td>Spanish (Chile)</td>\n<td>es_CL</td>\n</tr>\n<tr>\n<td>Spanish (Colombia)</td>\n<td>es_CO</td>\n</tr>\n<tr>\n<td>Spanish (Costa Rica)</td>\n<td>es_CR</td>\n</tr>\n<tr>\n<td>Spanish (Dominican Republic)</td>\n<td>es_DO</td>\n</tr>\n<tr>\n<td>Spanish (Ecuador)</td>\n<td>es_EC</td>\n</tr>\n<tr>\n<td>Spanish (El Salvador)</td>\n<td>es_SV</td>\n</tr>\n<tr>\n<td>Spanish (Guatemala)</td>\n<td>es_GT</td>\n</tr>\n<tr>\n<td>Spanish (Honduras)</td>\n<td>es_HN</td>\n</tr>\n<tr>\n<td>Spanish (Mexico)</td>\n<td>es_MX</td>\n</tr>\n<tr>\n<td>Spanish (Nicaragua)</td>\n<td>es_NI</td>\n</tr>\n<tr>\n<td>Spanish (Panama)</td>\n<td>es_PA</td>\n</tr>\n<tr>\n<td>Spanish (Paraguay)</td>\n<td>es_PY</td>\n</tr>\n<tr>\n<td>Spanish (Peru)</td>\n<td>es_PE</td>\n</tr>\n<tr>\n<td>Spanish (Puerto Rico)</td>\n<td>es_PR</td>\n</tr>\n<tr>\n<td>Spanish (Spain)</td>\n<td>es_ES</td>\n</tr>\n<tr>\n<td>Spanish (United States)</td>\n<td>es_US</td>\n</tr>\n<tr>\n<td>Spanish (Uruguay)</td>\n<td>es_UY</td>\n</tr>\n<tr>\n<td>Spanish (Venezuela)</td>\n<td>es_VE</td>\n</tr>\n<tr>\n<td>Spanish</td>\n<td>es</td>\n</tr>\n<tr>\n<td>Swedish (Sweden)</td>\n<td>sv_SE</td>\n</tr>\n<tr>\n<td>Swedish</td>\n<td>sv</td>\n</tr>\n<tr>\n<td>Thai (Thailand)</td>\n<td>th_TH</td>\n</tr>\n<tr>\n<td>Thai (Thailand,TH)</td>\n<td>th_TH_TH</td>\n</tr>\n<tr>\n<td>Thai</td>\n<td>th</td>\n</tr>\n<tr>\n<td>Turkish (Turkey)</td>\n<td>tr_TR</td>\n</tr>\n<tr>\n<td>Turkish</td>\n<td>tr</td>\n</tr>\n<tr>\n<td>Ukrainian (Ukraine)</td>\n<td>uk_UA</td>\n</tr>\n<tr>\n<td>Ukrainian</td>\n<td>uk</td>\n</tr>\n<tr>\n<td>Vietnamese (Vietnam)</td>\n<td>vi_VN</td>\n</tr>\n<tr>\n<td>Vietnamese</td>\n<td>vi</td>\n</tr>\n</tbody>\n</table>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/deploy/static-hosting/",
      "url": "https://cecil-945.pages.dev/documentation/deploy/static-hosting/",
      "title": "Static hosting",
      "summary": "Deploy to a static hosting (Surge).",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Static hosting\nThe built site can be uploaded as is to any static hosting service.\nSurge\n\nShipping web projects should be fast, easy, and low risk. Surge is static web publishing for Front-End Developers, right from the CLI.\n\n➡️ https:\/\/surge.sh\nTerminal:\nnpm install -g surge\nsurge _site\/\nOfficial documentation",
      "content_html": "<h1>Static hosting</h1>\n<p>The built site can be uploaded as is to any static hosting service.</p>\n<h2 id=\"surge\">Surge</h2>\n<blockquote>\n<p>Shipping web projects should be fast, easy, and low risk. Surge is static web publishing for Front-End Developers, right from the CLI.</p>\n</blockquote>\n<p>➡️ <a href=\"https://surge.sh\" target=\"_blank\" rel=\"noopener noreferrer\">https://surge.sh</a></p>\n<p>Terminal:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">npm install -g surge\nsurge _site/</code></pre>\n<p><a href=\"https://surge.sh/help/\" target=\"_blank\" rel=\"noopener noreferrer\">Official documentation</a></p>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/commands/build/",
      "url": "https://cecil-945.pages.dev/documentation/commands/build/",
      "title": "build",
      "summary": "Build the website.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "build\nBuilds the site.\nUsage\nDescription:\n  Builds the website\n\nUsage:\n  build [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                               Use the given path as working directory\n\nOptions:\n  -d, --drafts                       Include drafts\n  -u, --baseurl=BASEURL              Set the base URL\n  -o, --output=OUTPUT                Set the output directory\n      --optimize|--no-optimize       Enable (or disable --no-optimize) optimization of generated files\n      --dry-run                      Build without saving\n  -c, --config=CONFIG                Set the path to extra config files (comma-separated)\n      --clear-cache[=CLEAR-CACHE]    Clear cache before build (optional cache key as regular expression) [default: false]\n  -p, --page=PAGE                    Build a specific page\n      --render-subset=RENDER-SUBSET  Render a subset of pages\n      --show-pages                   Show list of built pages in a table\n  -m, --metrics                      Show build metrics (duration and memory) of each step\n      --notify                       Send desktop notification on build completion\n  -h, --help                         Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                        Do not output any message\n  -V, --version                      Display this application version\n      --ansi|--no-ansi               Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction               Do not ask any interactive question\n  -v|vv|vvv, --verbose               Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The build command generates the website in the output directory.\n\n    cecil.phar build\n    cecil.phar build path\/to\/the\/working\/directory\n    cecil.phar build --baseurl=https:\/\/example.com\/\n    cecil.phar build --output=_site\n\n  To build the website with optimization of generated files, you can use the --optimize option.\n  This is useful to reduce the size of the generated files and improve performance:\n\n    cecil.phar build --optimize\n    cecil.phar build --no-optimize\n\n  To build the website without overwriting files in the output directory, you can use the --dry-run option.\n  This is useful to check what would be built without actually writing files:\n\n    cecil.phar build --dry-run\n\n  To build the website with a specific subset of rendered pages, you can use the --render-subset option.\n  This is useful to build only a part of the website, for example, only \"hot\" pages or a specific section:\n\n    cecil.phar build --render-subset=subset\n\n  To show build steps metrics, run:\n\n    cecil.phar build --metrics",
      "content_html": "<h1>build</h1>\n<p>Builds the site.</p>\n<h2 id=\"usage\">Usage</h2>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Builds the website\n\nUsage:\n  build [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                               Use the given path as working directory\n\nOptions:\n  -d, --drafts                       Include drafts\n  -u, --baseurl=BASEURL              Set the base URL\n  -o, --output=OUTPUT                Set the output directory\n      --optimize|--no-optimize       Enable (or disable --no-optimize) optimization of generated files\n      --dry-run                      Build without saving\n  -c, --config=CONFIG                Set the path to extra config files (comma-separated)\n      --clear-cache[=CLEAR-CACHE]    Clear cache before build (optional cache key as regular expression) [default: false]\n  -p, --page=PAGE                    Build a specific page\n      --render-subset=RENDER-SUBSET  Render a subset of pages\n      --show-pages                   Show list of built pages in a table\n  -m, --metrics                      Show build metrics (duration and memory) of each step\n      --notify                       Send desktop notification on build completion\n  -h, --help                         Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                        Do not output any message\n  -V, --version                      Display this application version\n      --ansi|--no-ansi               Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction               Do not ask any interactive question\n  -v|vv|vvv, --verbose               Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The build command generates the website in the output directory.\n\n    cecil.phar build\n    cecil.phar build path/to/the/working/directory\n    cecil.phar build --baseurl=https://example.com/\n    cecil.phar build --output=_site\n\n  To build the website with optimization of generated files, you can use the --optimize option.\n  This is useful to reduce the size of the generated files and improve performance:\n\n    cecil.phar build --optimize\n    cecil.phar build --no-optimize\n\n  To build the website without overwriting files in the output directory, you can use the --dry-run option.\n  This is useful to check what would be built without actually writing files:\n\n    cecil.phar build --dry-run\n\n  To build the website with a specific subset of rendered pages, you can use the --render-subset option.\n  This is useful to build only a part of the website, for example, only \"hot\" pages or a specific section:\n\n    cecil.phar build --render-subset=subset\n\n  To show build steps metrics, run:\n\n    cecil.phar build --metrics</code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/getting-started/starter-kits/",
      "url": "https://cecil-945.pages.dev/documentation/getting-started/starter-kits/",
      "title": "Starter kits",
      "summary": "Get started quickly with a ready-to-use project template.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-10T00:00:00+00:00","content_text": "Starter kits\nTo get started quickly, use one of Cecil's ready-to-use starter kits.\nAvailable starter kits\n\nThe Butler: a publishing-ready starter blog.\nLinks: an open source Linktree alternative.\nPhoto Stream: a super simple self-hosted photo stream.\nStatidocs: build a documentation website quickly.\n\n\n\n\n\n",
      "content_html": "<h1>Starter kits</h1>\n<p>To get started quickly, use one of Cecil's ready-to-use <a href=\"/starter-kits/\">starter kits</a>.</p>\n<h2 id=\"available-starter-kits\">Available starter kits</h2>\n<ul>\n<li><a href=\"https://github.com/Cecilapp/the-butler#readme\" target=\"_blank\" rel=\"noopener noreferrer\">The Butler</a>: a publishing-ready starter blog.</li>\n<li><a href=\"https://github.com/Cecilapp/Links#readme\" target=\"_blank\" rel=\"noopener noreferrer\">Links</a>: an open source Linktree alternative.</li>\n<li><a href=\"https://github.com/Cecilapp/photo-stream#readme\" target=\"_blank\" rel=\"noopener noreferrer\">Photo Stream</a>: a super simple self-hosted photo stream.</li>\n<li><a href=\"https://github.com/Cecilapp/statidocs#readme\" target=\"_blank\" rel=\"noopener noreferrer\">Statidocs</a>: build a documentation website quickly.</li>\n</ul>\n<p><a href=\"https://github.com/Cecilapp/the-butler#readme\" target=\"_blank\" rel=\"noopener noreferrer\"><picture>\n<source type=\"image/avif\" srcset=\"/thumbnails/480x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.avif 480w, /thumbnails/768x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.avif 768w, /docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.avif 1014w\" width=\"1014\" height=\"713\" sizes=\"100vw\">\n<source type=\"image/webp\" srcset=\"/thumbnails/480x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.webp 480w, /thumbnails/768x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.webp 768w, /docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.webp 1014w\" width=\"1014\" height=\"713\" sizes=\"100vw\">\n<img src=\"/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.png\" alt=\"Starter blog example\" loading=\"lazy\" decoding=\"async\" class=\"dark:brightness-90\" width=\"1014\" height=\"713\" style=\";max-width:100%;height:auto;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAyCAYAAACqNX6+AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAOtUlEQVR4nNWc63rjNg5AD0A5bb92v2nfoO//gptYIrA/AJKQ7Ex3urOtywxDWbFuPMSV1Mjvv//u/IVFVbndbvz444/8/PPP/Otf/+LLr7/y22+/8duvv/Jrtl++fOHLly/88ssv/PTTT7y9vdFao7WN1hQRRVUAQQREBBEByrYIEjvOLZL/oh3ngLq9zjX/sjYZm7XzvOxwfO5wB3fD3XEzzB0zm9Ut/wbo9+jk71L8cVy4+6nm3vzq2OfzcC/n8NiRfXJpT7/rOR9v5XzOsp+vwbhsn640b3ZWL/u3h174O4pzuqnTgxYg5o44CI65oMRIDDmZp5qf/dNWkHmVIQmOe/5VZII4bz8dN/UxLlDqYFlw/VIrmNcAMooXKBcQAcMRNzBB1bJjBXFAfG5L7Ij2pK5SVQmISx5zhsVXt6fCu9725RkGlSqZPqFUaX9NCUkSU6GUG5961wwxA1FcDDdN2+HZ0bK2yU4fNmLaFJ3ARCpMcn/cjTj4pODLjgxJmb9iY477B5U1nsfPUBjP98ISMsfMuHGYkjGMIGYBAzAdxnt1/AMMWMZdzsZ/wpQBRUICBx1nyYMMEINYueFqHy77/BmUWfPvryghY4RVWzdAdDO6GTq8EekYROdNT2oAgdltMmCAiD4BImiqNUnpKkcXKXjmZV3crYtduUrMSQNMyVgD72UkpHoq6yYDRrUdPdWVmqWaESRd3tFLU91ULBOAJxRHKxBNtSVSjj+fp+48Sci3PGTdSM/QrUgMryYh5V5Phtycbs5hDuZod8BCo4TOeuisNYizk0UiZklJ0JQWFUFtSc06VgqUSylSWPd9XooKLYZchst+Go0vICG1VNfQCCnpHjDUHO+GYiH6Ci7p8Fa1IrV/RqAoUzJCOmxuLwmhBI15jmlDqj6S0+VOZerJsySlQh2+RW7Hw8o0JKsTXgJICjGOY05IhsPhzuGhrswcwcK+GLhIHifLI+Ki+2WpJEXO6moCukrIAiH1Bk/xzoN+y3YMABgDQoS89mplnitd+QGIv1tCpqcxXMFqzEM6dgt9q93ABTMwcUwEYyiAS7mM1uHiagEUUNLGrK+vw70C8NmJ8+8P1xowqlcX12wCbWwDTUhA67yS0P9mCSmykVAMpnTs5kh3ejfAcBNMnC4aai1k5hz1Akt3r3aBuXyeaoTp4Q7EMjrMy4imgJO8piwHQyRsm4oiKjQRmiqbCJsKW8LZChQd55Y/kJCaWDt149fyB/9l8Sc1pINpO/Z0dbUb7oYJdFE6zsGCN0CO85YnmADW86zYZbrBFCi+Rq0SmYE1klfnjfPnSWKvRNIT1bRRypb1ppJV2RRMhKYsieFTlfU8e3ryo0d+55J3+l+K49nBy5hP6RDD1OgChziHC0fC6M50Ah7vZej6qpaqKuM84glvaABQdxTLURyfp9TkeRHBMwvgoiAjE91CMppyU+XWlB+a8qbOWxNchVuxf5kwOAOpQVOt8dUVcZ4ysKfczJ8BMaRj1LOre3THMEw7uzgHyu7M2t3pMFXeWXWdsDwAmRAYBpYJQnAahnq2GG2AKUBcQjJWbQGkNZo2bk15a40fNqVbw5qHnLlEDStPkyuQU8C05hueAsmcvpstVfEnoMwO9DOUYUM83d2OcZiwC9zduTvcHXZ3jiElJbD87E7OYNa+AWJKSEpGSxgNY8u2FSCSQEwaljBcGmhDm7E149YaP2zO4Q1zJ5SUI5sSvh9zEkSRBWS6h6oFSEk5IGl8Q2XJgDHyTN8A5dRpfoYy1NBh4f5aNw43dhU+gA8XPsz5cLhbADmGd3Y997zaY0wh+bG6uKGuQD06vrmxecC40dkSjOJoBhaO0qUllIZrQ7QhbWNrxttmHBaeI97i/IRhb3jYD18x0AbPVJXS2gIzdK0DbuFyTmNmHQh39Fug1J47qS2GhICY07uzu3E34R14d3g3eDfnbrBnzNJHQMnVlvhpW8ouKbCGytIE0fCA4cbNjTc6NwkoTRYQQzFpdBomGyYG6mhztuYxsPJ6Oj2tMPBdhD5d4nj27ew365SQWTPVMIi4WIp4GXW5H84zb9/A5ASle/rlBt2cHedDjA+Hf1vU976AHKm6lgt8nhlcUlJGwCmuSK8qDXpzZ0sgNw+VaXRMOl1SbRUJMXEOnC7FNBg0E3p6YqrGdnTeVHhT5TCna0y0eapcBbYFA0RL0k0KEJUiIcQVJw6fascMROyb7ckDFAIKKSn7gOLGhwnv5rx3+Og+7ciRLrOXn8czP1Ndsa1pR9oEYtw8HIqIeDqI4RguAS4kxMMdBw4RLO2KpKpDjdaNrRtvTWMAZY7O/HFQbqdgKaVkQNFiR0bQ5MqMYqvOCTDpAvLtRr56SMZSkd3hAHbCq7qbcTf4SJV1/wTI2cRHPcM4A5rqCmik04Jl7GHT7VWJQNHTADlOFw93HKfndSP/5qg6ew9vcYHw5YhcXMNtBk4TTIGjpSUEQ1zDkJPOQR2VKXZh5J+rr8v1/xCQEaMvDL1PiTk8YOzpbfUCZBx9BRJQ/HKVFX8IQ+2t74aHFR3dcVrauTFAh916VvFQpyO2MvMZ/H7WB1uFMQ11iT9GmqG6ilq+66qoe2RfNSQjFgbIJ5f878sAMqL37pJtSESHHJUBzT+BUOtlCcVsa6Q+YHRqhzPreDSHeG5fanIClWeQWDCe3R7Dy8oOPqUQSlsDqrkrJUc9ok7xqKoBo67W+F/LSZ15gcSq9pXjPz/rasfvoS6rC23D5R/fG4cKJSA9/3BqL1euvsWlbNdvPY4nzxUalyMl3YlLBvU8Z/3/gTIlh9Vx3+casrovxcBZ7VB4TsRiU0qgdL2ni/jn7mojDxWYqZDrMhwf882fXUPmr0v7/WBc2yfS/p2KlN9KKCyZkhKqR+YM4Ix9HBBHdKWUznEOxUbXK53L5u6z604L0kpaBFjzBpfvjhUUz7vt/1e+Hwh5+CSZlwibMsLGMd8fKnLcxZyTGaO1ZIuf1wr8sWyeJ4km0iAi8qCTT6n4Am6tUT3DOUN69bI66QQj9zphF1cm4WwfjJgaGFnfWNA30vWjPs69PCtbTaGPnFTt/CEND0DgtHD4cR3ut0Xtf32R07aQU6yQcx6ChHIKW2ExbnuqI/VhvQKYKbiGOyAS8YeO3BjEPh7V11WEtqvRdXd677NjxQxTncHj+M6AMmCcW14cRi1LLgJKgJHhzLiACW4RS2AxWaYWjnYUxdVBBWmGqCFuU1LWrGCJLD65mxOQusB4fJbM7F5nD+sa1ZmOH6rMvj198teX5XyMnzGaq5Tg+ayWWe3eceuY9TXqREAVadHxqCLa0M1QjzmVk7TwGGKMO5pu7+jAYUMmjOvMYflubZ+/OvCKpXqBSzqGZKisqF2GB5vTAPQD7wfWD+g99BgjJmvIFn6wioI2pD+DkdO1ctFWQ2Vdb3eqqicgrkDq9uuDgMfxOGDItB9LbfmE4d2w3vHjgH2HYwc7wEItoTFlq5aL+ETx1sA2xPoy8u4zU365DYYd+3SRQwXzRx39+iBquUqGsNZNRdZ3wBCPWVHvHT92uO/4fof9jvc95oKcyFi0jZZAmii+NTg26LcJrs7HT3vCickfLwP6Z3X218ozVaUBQWTBkDDmM69lPVTUHjDs4wPfP/Djjvcc/apo2/CeQFSxbcNvHazHaxQWqy4f76Iaevm712X9+XL1GL8+bD6xG1Kkw1ktuczTOt4P/NjxfcfuH9jHO3Z/x/c73o8pIW274R72t22N2+2GHUdIl4XPXFXW4zKk+PyPAPIsKfPMS/n60bEdHaC5inCsJJSy1CciC9xw69APbN+xlI7+8Y59/Ds+9yM6Whvt1nFAtbHdNvrxFjD7kI4RC1QoQ0Lknychz2BUF/VZkrFG2+OBRXSu81VymWca9OYgpKFOF9eOAzt2bP/guAeQ/vEeQI4DcFQbbh0RaK3R325xTD9wSwnJvPsjjLPE/GOAwBnAtQJz0dk5woCakQ4YWgx5iT985K9sqaseQPq+0/cP+j3B3D9CZRFSEWCUvt3o+x4gM2Zxt3SRVzpUquv7T5OQ64iKZTSs2TuIXNLpu2UGdKqqnKIuEjLjgjFyZyDYE8qOHzt23LF9px93+h7V+xHqrW1x/W3DMk6ZMIa6oq4RLpIh52d7eSBTPUnUsRa2ETfvxIP46ftj2T8FxCctlDd3AXy+ZOq9Zz2y7lOFed+nhADQWnxOCPEfBdiSjjJFOJMew9dNGl+NQ16hnGD4WtbfxNkkpAICzvx+AaEX6SAN+pyyZs1rDD0XWWxDrIP3aK1HZN57BoQ9YguzOEyj08cLOPIEQlVVFHU1rjue9WWBjEG0VvqF8d3UuTn0NBxVbUVWdcBYNoMC5ME380gcQiTTLTvT80UhrCM+qs1k4chjVedipNtX8vACI/ViSEidvFqu7+sDmVIBm5Gr/sZSobWCAwYQmepNsiUlxROIIwGCTBoKsVhB1sRwLvFGiXmf4Ra30ZFDzQjzbaymY/nUuAcpg2R0vl8gnOtLAXl2gy6CakhHU2HDuREQlLWKYxp8WW8rzeRoDss1ZZRBuAumMW9jEhiQtSBVc55QJaYaxrnRWAMdoz62W9N4/UCVTeNeW4FSpXdISbVjYwi+DBC5bA/9Gp2QRlzghsyYQ2Wsm12jrkl5ZWx2QPzVKHPjngu0DbqkUZfzypE223CpNU8qqnjT7FjJVw+UrbX5gs4mOtfszncZ+XqFFwIyysktzA+hsoTmQlNny45V97mQOWB4MfzrncIhHZa1eyxW6ASM2Ru57UOFEVBDSmPU0xRpinsBsilta/nfRwWcNqVkZJEf50Dk9NTRvgSQhxuUBWS8vjwCuJYdb0r8JzRCzkHEvPUmzPf4htoamtskYIhANynTmrnQj/EqW0igSW5LBJAuimvWpqBLQlRbWaRe4pzpXHBSTlViankJILUsD4Xlqk49HFOrmrknl2LQZb1vMZwAHe6tLDuDgLukEQ/191V1IqUdgFWIZZplwOjZZq3t8hxIeaYrlPj9H6oxhUTjWngBAAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center;background-size:cover;\" srcset=\"/thumbnails/480x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.png 480w, /thumbnails/768x/docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.png 768w, /docs/cecil-newblog.c85c682e3bcb416589e2bc2bbcc2cfca.png 1014w\" sizes=\"100vw\">\n</picture></a></p>",
      "language": "en"
    },
    {
      "id": "https://cecil-945.pages.dev/documentation/commands/doctor/",
      "url": "https://cecil-945.pages.dev/documentation/commands/doctor/",
      "title": "doctor",
      "summary": "Diagnose the configuration, front matter, SEO and links.",
      "date_published": "2020-12-19T00:00:00+00:00",
      "date_modified": "2026-10-11T00:00:00+00:00","content_text": "doctor\nDiagnoses the current site and Cecil environment.\nUsage\nDescription:\n  Diagnoses the site configuration\n\nUsage:\n  doctor [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                       Use the given path as working directory\n\nOptions:\n  -c, --config=CONFIG        Set the path to an extra configuration file\n  -h, --help                 Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                Do not output any message\n  -V, --version              Display this application version\n      --ansi|--no-ansi       Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction       Do not ask any interactive question\n  -v|vv|vvv, --verbose       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The doctor command diagnoses the current site and Cecil environment.\n\n    cecil.phar doctor\n    cecil.phar doctor path\/to\/the\/working\/directory\n\n  To inspect a site with an extra configuration file, run:\n\n    cecil.phar doctor --config=config.yml\ndoctor:frontmatter\nValidates pages front matter syntax.\nDescription:\n  Validates pages front matter syntax\n\nUsage:\n  doctor:frontmatter|doctor:fm [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                  Use the given path as working directory\n\nOptions:\n  -c, --config=CONFIG   Set the path to an extra configuration file\n  -p, --page=PAGE       Validate a single page relative to the pages directory\n      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction  Do not ask any interactive question\n  -h, --help            Display help for the given command. When no command is given display help for the list command\n  -q, --quiet           Do not output any message\n  -V, --version         Display this application version\n  -v|vv|vvv             Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\ndoctor:seo\nAudits rendered HTML pages for common SEO issues.\nDescription:\n  Audits rendered HTML pages for common SEO issues\n\nUsage:\n  doctor:seo [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                  Use the given path as working directory\n\nOptions:\n  -c, --config=CONFIG   Set the path to an extra configuration file\n  -p, --page=PAGE       Audit a single page relative to the pages directory\n      --format=FORMAT   Output format: text (default) or json\n      --feedback        Include findings with feedback level\n      --include-virtual Include virtual pages (paginated, taxonomies) in audit\n      --fail-on=FAIL-ON Return a failure exit code when findings of this level or higher are found: bad (default), ok, feedback or never\nThe command builds the site in dry-run mode, then audits the rendered HTML output for a focused set of checks: title tag, meta description, canonical URL, heading structure, Open Graph tags, image alt attributes and estimated content length.\nBy default, virtual pages (paginated, taxonomy pages) are excluded from the audit. Use --include-virtual to include them.\nBy default, findings with level feedback are not listed.\nUse --feedback to include findings with level feedback in addition to other findings.\nOutput results as JSON for CI integration using --format=json.\nBy default, the command returns a failure exit code (1) when findings with level bad are found. Use --fail-on=ok to also fail on ok findings, --fail-on=feedback to fail on any finding, or --fail-on=never to always succeed.\nConfiguration\nCustomize audit thresholds and enabled checks in your configuration file:\ndoctor:\n  seo:\n    title: { min: 30, max: 60 }\n    description: { min: 120, max: 160 }\n    content: { min_words: 300 }\n    checks:\n      title: true\n      description: true\n      canonical: true\n      h1: true\n      og_tags: true\n      img_alt: true\n      content_length: true\n      lang_attribute: true\ndoctor:links\nChecks internal and external links of the rendered pages.\nDescription:\n  Checks internal and external links of the rendered pages\n\nUsage:\n  doctor:links [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                           Use the given path as working directory\n\nOptions:\n  -c, --config=CONFIG            Set the path to an extra configuration file\n  -p, --page=PAGE                Check links of a single page relative to the pages directory\n      --internal                 Check internal links\n      --external                 Check external links\n      --exclude=EXCLUDE          Exclude links matching this regular expression (multiple values allowed)\n      --timeout=TIMEOUT          External links request timeout, in seconds\n      --concurrency=CONCURRENCY  Number of concurrent external links requests\n      --no-cache                 Do not use cached external links results\n      --format=FORMAT            Output format: text (default) or json\n      --include-virtual          Include virtual pages (paginated, taxonomies) in audit\n      --feedback                 Include findings with feedback level\n      --fail-on=FAIL-ON          Return a failure exit code when findings of this level or higher are found: bad (default), ok, feedback or never\nThe command builds the site in dry-run mode, then checks the links found in the rendered HTML pages: links (&lt;a&gt;, &lt;area&gt;, &lt;link&gt;), images (src and srcset), scripts, iframes, media and inline styles. CSS files linked from the pages are also analyzed (url() and @import).\n\nInternal links must target a generated page, a static file or an asset. Fragments (#anchor) must match an element ID of the target page. Absolute URLs starting with baseurl are considered as internal links.\nExternal links are requested over HTTP (HEAD, then GET if the server refuses HEAD), concurrently. Successful results are cached (24 hours by default): use --no-cache to check them again.\n\nBy default, only internal links are checked:\nphp cecil.phar doctor:links                       # internal links (default)\nphp cecil.phar doctor:links --external            # external links only\nphp cecil.phar doctor:links --internal --external # both\nFindings levels:\n\n\n\nLevel\nMeaning\n\n\n\n\nbad\nBroken link: target not found, missing anchor, HTTP error (4xx, 5xx), unresolved host, etc.\n\n\nok\nWorking link that could be improved: redirection, missing trailing slash\n\n\nfeedback\nUnverifiable link: access denied (401, 403, 429), probably by a bot protection, or timeout\n\n\n\nLinks using other schemes than HTTP (e.g.: mailto:, tel:, data:) are ignored. Use --exclude (regular expression, case insensitive) to ignore other links.\n--page, --include-virtual, --feedback, --format and --fail-on options behave as with doctor:seo (see above).\nChecking external links requires the PHP cURL extension.\nConfiguration\nCustomize default settings in your configuration file:\ndoctor:\n  links:\n    internal: true    # check internal links\n    external: false   # check external links\n    timeout: 10       # external links request timeout, in seconds\n    concurrency: 10   # number of concurrent external links requests\n    cache_ttl: 86400  # external links successful results cache TTL, in seconds\n    exclude: []       # links to ignore (list of regular expressions), e.g.: ['linkedin\\.com', '^https:\/\/localhost']\n    useragent: ~      # User-Agent header of external links requests (`assets.remote.useragent.default` by default)",
      "content_html": "<h1>doctor</h1>\n<p>Diagnoses the current site and Cecil environment.</p>\n<h2 id=\"usage\">Usage</h2>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Diagnoses the site configuration\n\nUsage:\n  doctor [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                       Use the given path as working directory\n\nOptions:\n  -c, --config=CONFIG        Set the path to an extra configuration file\n  -h, --help                 Display help for the given command. When no command is given display help for the list command\n  -q, --quiet                Do not output any message\n  -V, --version              Display this application version\n      --ansi|--no-ansi       Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction       Do not ask any interactive question\n  -v|vv|vvv, --verbose       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug\n\nHelp:\n  The doctor command diagnoses the current site and Cecil environment.\n\n    cecil.phar doctor\n    cecil.phar doctor path/to/the/working/directory\n\n  To inspect a site with an extra configuration file, run:\n\n    cecil.phar doctor --config=config.yml</code></pre>\n<h2 id=\"doctor-frontmatter\">doctor:frontmatter</h2>\n<p>Validates pages front matter syntax.</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Validates pages front matter syntax\n\nUsage:\n  doctor:frontmatter|doctor:fm [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                  Use the given path as working directory\n\nOptions:\n  -c, --config=CONFIG   Set the path to an extra configuration file\n  -p, --page=PAGE       Validate a single page relative to the pages directory\n      --ansi|--no-ansi  Force (or disable --no-ansi) ANSI output\n  -n, --no-interaction  Do not ask any interactive question\n  -h, --help            Display help for the given command. When no command is given display help for the list command\n  -q, --quiet           Do not output any message\n  -V, --version         Display this application version\n  -v|vv|vvv             Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug</code></pre>\n<h2 id=\"doctor-seo\">doctor:seo</h2>\n<p>Audits rendered HTML pages for common SEO issues.</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Audits rendered HTML pages for common SEO issues\n\nUsage:\n  doctor:seo [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                  Use the given path as working directory\n\nOptions:\n  -c, --config=CONFIG   Set the path to an extra configuration file\n  -p, --page=PAGE       Audit a single page relative to the pages directory\n      --format=FORMAT   Output format: text (default) or json\n      --feedback        Include findings with feedback level\n      --include-virtual Include virtual pages (paginated, taxonomies) in audit\n      --fail-on=FAIL-ON Return a failure exit code when findings of this level or higher are found: bad (default), ok, feedback or never</code></pre>\n<p>The command builds the site in dry-run mode, then audits the rendered HTML output for a focused set of checks: title tag, meta description, canonical URL, heading structure, Open Graph tags, image alt attributes and estimated content length.</p>\n<p>By default, virtual pages (paginated, taxonomy pages) are excluded from the audit. Use <code translate=\"no\">--include-virtual</code> to include them.</p>\n<p>By default, findings with level <code translate=\"no\">feedback</code> are not listed.</p>\n<p>Use <code translate=\"no\">--feedback</code> to include findings with level <code translate=\"no\">feedback</code> in addition to other findings.</p>\n<p>Output results as JSON for CI integration using <code translate=\"no\">--format=json</code>.</p>\n<p>By default, the command returns a failure exit code (<code translate=\"no\">1</code>) when findings with level <code translate=\"no\">bad</code> are found. Use <code translate=\"no\">--fail-on=ok</code> to also fail on <code translate=\"no\">ok</code> findings, <code translate=\"no\">--fail-on=feedback</code> to fail on any finding, or <code translate=\"no\">--fail-on=never</code> to always succeed.</p>\n<h3 id=\"configuration\">Configuration</h3>\n<p>Customize audit thresholds and enabled checks in your configuration file:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">doctor:</span>\n  <span class=\"hljs-attr\">seo:</span>\n    <span class=\"hljs-attr\">title:</span> <span class=\"hljs-string\">{</span> <span class=\"hljs-attr\">min:</span> <span class=\"hljs-number\">30</span><span class=\"hljs-string\">,</span> <span class=\"hljs-attr\">max:</span> <span class=\"hljs-number\">60</span> <span class=\"hljs-string\">}</span>\n    <span class=\"hljs-attr\">description:</span> <span class=\"hljs-string\">{</span> <span class=\"hljs-attr\">min:</span> <span class=\"hljs-number\">120</span><span class=\"hljs-string\">,</span> <span class=\"hljs-attr\">max:</span> <span class=\"hljs-number\">160</span> <span class=\"hljs-string\">}</span>\n    <span class=\"hljs-attr\">content:</span> <span class=\"hljs-string\">{</span> <span class=\"hljs-attr\">min_words:</span> <span class=\"hljs-number\">300</span> <span class=\"hljs-string\">}</span>\n    <span class=\"hljs-attr\">checks:</span>\n      <span class=\"hljs-attr\">title:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">description:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">canonical:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">h1:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">og_tags:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">img_alt:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">content_length:</span> <span class=\"hljs-literal\">true</span>\n      <span class=\"hljs-attr\">lang_attribute:</span> <span class=\"hljs-literal\">true</span></code></pre>\n<h2 id=\"doctor-links\">doctor:links</h2>\n<p>Checks internal and external links of the rendered pages.</p>\n<pre><code class=\"language-plaintext hljs plaintext\" translate=\"no\">Description:\n  Checks internal and external links of the rendered pages\n\nUsage:\n  doctor:links [options] [--] [&lt;path&gt;]\n\nArguments:\n  path                           Use the given path as working directory\n\nOptions:\n  -c, --config=CONFIG            Set the path to an extra configuration file\n  -p, --page=PAGE                Check links of a single page relative to the pages directory\n      --internal                 Check internal links\n      --external                 Check external links\n      --exclude=EXCLUDE          Exclude links matching this regular expression (multiple values allowed)\n      --timeout=TIMEOUT          External links request timeout, in seconds\n      --concurrency=CONCURRENCY  Number of concurrent external links requests\n      --no-cache                 Do not use cached external links results\n      --format=FORMAT            Output format: text (default) or json\n      --include-virtual          Include virtual pages (paginated, taxonomies) in audit\n      --feedback                 Include findings with feedback level\n      --fail-on=FAIL-ON          Return a failure exit code when findings of this level or higher are found: bad (default), ok, feedback or never</code></pre>\n<p>The command builds the site in dry-run mode, then checks the links found in the rendered HTML pages: links (<code translate=\"no\">&lt;a&gt;</code>, <code translate=\"no\">&lt;area&gt;</code>, <code translate=\"no\">&lt;link&gt;</code>), images (<code translate=\"no\">src</code> and <code translate=\"no\">srcset</code>), scripts, iframes, media and inline styles. CSS files linked from the pages are also analyzed (<code translate=\"no\">url()</code> and <code translate=\"no\">@import</code>).</p>\n<ul>\n<li><strong>Internal links</strong> must target a generated page, a static file or an asset. Fragments (<code translate=\"no\">#anchor</code>) must match an element ID of the target page. Absolute URLs starting with <code translate=\"no\">baseurl</code> are considered as internal links.</li>\n<li><strong>External links</strong> are requested over HTTP (<code translate=\"no\">HEAD</code>, then <code translate=\"no\">GET</code> if the server refuses <code translate=\"no\">HEAD</code>), concurrently. Successful results are cached (24 hours by default): use <code translate=\"no\">--no-cache</code> to check them again.</li>\n</ul>\n<p>By default, only internal links are checked:</p>\n<pre><code class=\"language-bash hljs bash\" translate=\"no\">php cecil.phar doctor:links                       <span class=\"hljs-comment\"># internal links (default)</span>\nphp cecil.phar doctor:links --external            <span class=\"hljs-comment\"># external links only</span>\nphp cecil.phar doctor:links --internal --external <span class=\"hljs-comment\"># both</span></code></pre>\n<p>Findings levels:</p>\n<table>\n<thead>\n<tr>\n<th>Level</th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code translate=\"no\">bad</code></td>\n<td>Broken link: target not found, missing anchor, HTTP error (4xx, 5xx), unresolved host, etc.</td>\n</tr>\n<tr>\n<td><code translate=\"no\">ok</code></td>\n<td>Working link that could be improved: redirection, missing trailing slash</td>\n</tr>\n<tr>\n<td><code translate=\"no\">feedback</code></td>\n<td>Unverifiable link: access denied (401, 403, 429), probably by a bot protection, or timeout</td>\n</tr>\n</tbody>\n</table>\n<p>Links using other schemes than HTTP (e.g.: <code translate=\"no\">mailto:</code>, <code translate=\"no\">tel:</code>, <code translate=\"no\">data:</code>) are ignored. Use <code translate=\"no\">--exclude</code> (regular expression, case insensitive) to ignore other links.</p>\n<p><code translate=\"no\">--page</code>, <code translate=\"no\">--include-virtual</code>, <code translate=\"no\">--feedback</code>, <code translate=\"no\">--format</code> and <code translate=\"no\">--fail-on</code> options behave as with <code translate=\"no\">doctor:seo</code> (see above).</p>\n<aside class=\"note note-info\"><p>Checking external links requires the PHP cURL extension.</p></aside>\n<h3 id=\"configuration-1\">Configuration</h3>\n<p>Customize default settings in your configuration file:</p>\n<pre><code class=\"language-yaml hljs yaml\" translate=\"no\"><span class=\"hljs-attr\">doctor:</span>\n  <span class=\"hljs-attr\">links:</span>\n    <span class=\"hljs-attr\">internal:</span> <span class=\"hljs-literal\">true</span>    <span class=\"hljs-comment\"># check internal links</span>\n    <span class=\"hljs-attr\">external:</span> <span class=\"hljs-literal\">false</span>   <span class=\"hljs-comment\"># check external links</span>\n    <span class=\"hljs-attr\">timeout:</span> <span class=\"hljs-number\">10</span>       <span class=\"hljs-comment\"># external links request timeout, in seconds</span>\n    <span class=\"hljs-attr\">concurrency:</span> <span class=\"hljs-number\">10</span>   <span class=\"hljs-comment\"># number of concurrent external links requests</span>\n    <span class=\"hljs-attr\">cache_ttl:</span> <span class=\"hljs-number\">86400</span>  <span class=\"hljs-comment\"># external links successful results cache TTL, in seconds</span>\n    <span class=\"hljs-attr\">exclude:</span> <span class=\"hljs-string\">[]</span>       <span class=\"hljs-comment\"># links to ignore (list of regular expressions), e.g.: ['linkedin\\.com', '^https://localhost']</span>\n    <span class=\"hljs-attr\">useragent:</span> <span class=\"hljs-string\">~</span>      <span class=\"hljs-comment\"># User-Agent header of external links requests (`assets.remote.useragent.default` by default)</span></code></pre>",
      "language": "en"
    }
  ]
}
