⌘K

Templates

Templates

header.php

Generates all HTML from <!DOCTYPE html> to the opening of the main content area.

Injected variables

VariableTypeDescription
$settingsarrayAll site settings
$dataarraySite content — see note below
$pageTitlestringCurrent page title
$metaTitlestringCalculated SEO <title>
$metaDescriptionstringCalculated SEO meta description
$metaKeywordsstringMeta keywords (if set on the item)
$ogImagestringOpen Graph image URL
$ogTitlestringOpen Graph title
$ogDescriptionstringOpen Graph description
$typestringCurrent content type (article, project, page, '')
$slugstringCurrent item slug (empty on list pages)
$headerScriptsarrayAdditional scripts to inject — use render_header_scripts()

ℹ️

On list pages and the homepage, $data['article'] contains the full lightweight index. On single-item pages, it contains only that one item. Use slloadindex('article') when you need the full catalogue from an uncertain context.

Minimal <head>

<!DOCTYPE html>
<html lang="<?php echo htmlspecialchars($settings['site_language'] ?? 'en'); ?>">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?php echo $metaTitle; ?></title>
    <?php echo render_meta_tags($settings, $metaTitle, $metaDescription); ?>
    <link rel="stylesheet" href="<?php echo getBaseUrl(); ?>css/lightbox.css">
    <?php echo render_header_scripts($headerScripts); ?>
</head>

render_header_scripts() injects in this order: system CSS, theme style.css, js/main.js, conditional gallery scripts, theme script.js, JS i18n bridge.

Admin bar compatibility

SynaptikCMS injects a 36px admin toolbar at the top of the viewport when an administrator is logged in. Your theme needs to handle two things: offset any fixed navigation so it clears the bar, and ensure the bar renders above your theme's elements.


1. Offset your fixed navigation

The CMS sets a CSS custom property --snk-adminbar-height to 36px in :root when the admin bar is active, and 0px by default (via the var() fallback). Use it anywhere your theme has a position: fixed element with a top offset:

.site-header {
  position: fixed;
  top: var(--snk-adminbar-height, 0px);
}

If a secondary element sits below the header (e.g. a sidebar starting at top: var(--header-h)), offset it with calc():

.docs-sidebar {
  position: fixed;
  top: calc(var(--snk-adminbar-height, 0px) + var(--header-h));
}

Elements using position: sticky do not need this adjustment — they follow the normal document flow, which is already offset by body { padding-top } when the admin bar is active.

Do not declare --snk-adminbar-height in your theme's :root. The fallback in var(--snk-adminbar-height, 0px) handles the default case automatically.


2. Render the admin bar before your navigation

If your theme's navigation uses backdrop-filter, it creates a CSS stacking context that can cause the admin bar to render behind it — regardless of z-index. To prevent this, call render_adminbar() as the first child of <body> in your header.php, before any theme markup:

</head>
<body>
<?php render_adminbar(); ?>

<header class="site-header">
  ...
</header>

This ensures the admin bar is earlier in the DOM than any stacking context your theme creates. It is a no-op for non-admin visitors — nothing is output if no admin session is active.

If your theme has no backdrop-filter on its navigation, this call is optional — the CMS emits the bar automatically after the header template. However, calling render_adminbar() explicitly is recommended for all themes as a best practice.


footer.php

Injected variables

VariableTypeDescription
$settingsarraySite settings
$dataarraySite content
$currentYearstringCurrent year (date('Y'))
$baseUrlstringBase URL with trailing slash

Minimal structure

    </main>
</div>

<?php echo render_search_ui(); ?>

</body>
</html>

ℹ️

rendersearchui() must be called before </body>. It generates the search overlay HTML that powers Ctrl+K, regardless of whether the search icon is visible.


home.php

Rendered between header.php and footer.php on the homepage. Injected: $data, $settings.

<?php
$articles = $data['article'] ?? [];
usort($articles, fn($a, $b) => strcmp($b['date'] ?? '', $a['date'] ?? ''));
$recent = array_slice($articles, 0, 5);
?>
<h1><?php echo htmlspecialchars($settings['site_title']); ?></h1>
<?php foreach ($recent as $article): ?>
    <?php echo render_article_card($article); ?>
<?php endforeach; ?>

content-articles.php, content-pages.php, content-projects.php

Single-item views. Receive only $item — the full item array.

<article>
    <?php render_featured_image($item); ?>
    <?php render_content_title($item); ?>
    <?php render_content_date($item); ?>
    <?php render_content_category($item); ?>
    <?php render_content_tags($item); ?>
    <div class="prose">
        <?php echo render_content_html($item['content'] ?? '', $item); ?>
    </div>
</article>

🚫

Always use rendercontenthtml($item['content'], $item) — never echo $item['content'] directly.


content-list.php

Injected variables

VariableTypeDescription
$list_typestring'article', 'project', 'page', 'category', 'tag'
$articlesarrayArticles to display (sorted by date desc)
$projectsarrayProjects to display (sorted by date desc)
$itemsarrayAll items with _content_type key
$filter_valuestringCurrent category or tag slug
$dataarrayFull data store
$settingsarraySite settings

Page templates (page-templates/)

<?php /* Template Name: Contact */ ?>
<div class="contact-page">
    <h1><?php echo htmlspecialchars($item['title']); ?></h1>
    <?php echo render_content_html($item['content'] ?? '', $item); ?>
    <?php echo render_contact_form_html(); ?>
</div>

Available variable: $item. Call loadSettings() if you need $settings.