Architecture
Layouts & Templates
How Bolt CMS composes pages from headers, layouts, and footers — and how to build your own custom ones.
Layout System Overview
Every page in Bolt CMS is assembled from three parts:
- Header — navigation, branding, and site-wide top bar.
- Layout — the structural template that wraps the page content.
- Footer — bottom links, copyright, and optional widgets.
The entry point (index.php) loads these in sequence after the page file has been required:
<body>
<?php
require_once(__DIR__ . '/headers/' . $page['config']['header'] . '.php');
require_once(__DIR__ . '/layouts/' . $page['config']['layout'] . '.php');
require_once(__DIR__ . '/footers/' . $page['config']['footer'] . '.php');
?>
</body>
Each page controls which header, layout, and footer it uses by setting values in $page['config']. If a page does not set these, Bolt falls back to its defaults — the default header, layout, and footer.
The canvas layout
Bolt ships a bare canvas layout that does nothing but echo your page content:
<?php echo $page['content']; ?>
A page that sets 'canvas' renders through it, so the page itself owns all of its structure — ideal for the homepage, landing pages, and any full-bleed design. The default layout is different: it is the documentation shell (a sidebar nav, a page heading, and prev/next links), and it is the layout a page gets when it sets none.
Everything beyond this is a custom layout: a PHP file you add to layouts/ that wraps $page['content'] in whatever structure you need. The boilerplates below are ready to drop in.
How to Select a Layout
Set the layout in your page file before the output buffer:
<?php
// Wrap this page in the "centered" custom layout
$page['config']['layout'] = 'centered';
ob_start();
?>
<!-- Page content here -->
<?php
$page['content'] = ob_get_clean();
?>
Headers, layouts, and footers are independent — you can mix and match any combination. Set a value to an empty string to skip that part entirely:
$page['config']['header'] = 'default';
$page['config']['layout'] = 'sidebar';
$page['config']['footer'] = ''; // render no footer
Creating a Custom Layout
A layout is just a PHP file in layouts/. It receives the full $page array in scope and is responsible for rendering $page['content']. The smallest useful custom layout simply wraps the content in a container:
<!-- layouts/custom.php -->
<div css="max-width: 64rem; margin-left: auto; margin-right: auto; padding-left: 1.5rem; padding-right: 1.5rem; padding-top: 3rem; padding-bottom: 3rem;">
<?php echo $page['content']; ?>
</div>
Then reference it from any page:
$page['config']['layout'] = 'custom';
Your layout has access to all $page['config'] values, global constants like SITEURL and ROOT_DIR, and any variables defined in index.php such as $currentPath. The boilerplates below put these to work.
Custom Layout Boilerplates
Three ready-to-use layouts you can copy straight into layouts/ and start using immediately. Each is small enough to read in one sitting and tweak to taste. They style their markup with the Paradigm CSS css="" attribute — the same approach the rest of your pages use.
centered — single-column reading
A narrow, centered column for long-form prose: blog posts, guides, changelog entries, and legal pages. It renders an optional title header from pageTitle / pageDescription, then your content.
<!-- layouts/centered.php -->
<div css="max-width: 42rem; margin-left: auto; margin-right: auto; padding-left: 1.5rem; padding-right: 1.5rem; padding-top: 4rem; padding-bottom: 4rem;">
<?php if (!empty($page['config']['pageTitle'])) : ?>
<header css="margin-bottom: 2.5rem;">
<h1 css="font-size: 2.25rem; font-weight: 700; letter-spacing: -0.025em; color: #0f172a;">
<?php echo $page['config']['pageTitle']; ?>
</h1>
<?php if (!empty($page['config']['pageDescription'])) : ?>
<p css="margin-top: 0.75rem; font-size: 1.125rem; color: #64748b;"><?php echo $page['config']['pageDescription']; ?></p>
<?php endif; ?>
</header>
<?php endif; ?>
<?php echo $page['content']; ?>
</div>
sidebar — two-column with navigation
A left navigation rail beside your content — ideal for documentation and knowledge bases. The nav is data-driven: define a nav array in the page config and the layout renders it, highlighting the current page using the global $currentPath.
<!-- layouts/sidebar.php -->
<?php $nav = $page['config']['nav'] ?? []; ?>
<div css="max-width: 80rem; margin-left: auto; margin-right: auto; display: block lg:flex; gap: 3rem; padding-left: 1.5rem; padding-right: 1.5rem; padding-top: 3rem; padding-bottom: 3rem;">
<aside css="width: lg:15rem; flex-shrink: 0;">
<nav css="display: flex; flex-direction: column; gap: 0.25rem;">
<?php foreach ($nav as $item) :
$isActive = ($currentPath === $item['href']);
?>
<a href="<?php echo SITEURL . $item['href']; ?>"
css="display: block; border-radius: 0.375rem; padding-left: 0.75rem; padding-right: 0.75rem; padding-top: 0.5rem; padding-bottom: 0.5rem; font-size: 0.875rem; <?php echo $isActive
? 'background: #f1f5f9; font-weight: 500; color: #0f172a;'
: 'color: #64748b #0f172a:hover;'; ?>">
<?php echo $item['label']; ?>
</a>
<?php endforeach; ?>
</nav>
</aside>
<main css="flex: 1_1_0%; min-width: 0;">
<?php if (!empty($page['config']['pageTitle'])) : ?>
<h1 css="font-size: 1.875rem; font-weight: 700; letter-spacing: -0.025em; color: #0f172a; margin-bottom: 2rem;">
<?php echo $page['config']['pageTitle']; ?>
</h1>
<?php endif; ?>
<?php echo $page['content']; ?>
</main>
</div>
Feed it a navigation list from any page that uses it:
$page['config']['layout'] = 'sidebar';
$page['config']['nav'] = [
['label' => 'Overview', 'href' => '/guide'],
['label' => 'Installation', 'href' => '/guide/install'],
['label' => 'Configuration', 'href' => '/guide/config'],
];
split — split-screen
A 50/50 split with your content on one side and a visual panel on the other. Useful for landing heroes, sign-in screens, and feature spotlights. The visual panel collapses on mobile, and an optional splitImage config value fills it with a background image.
<!-- layouts/split.php -->
<?php
$paneStyle = !empty($page['config']['splitImage'])
? "background-image: url('" . $page['config']['splitImage'] . "'); background-size: cover; background-position: center;"
: '';
?>
<div css="display: block lg:grid; grid-template-columns: lg:repeat(2,minmax(0,1fr)); min-height: lg:calc(100vh-3.5rem);">
<!-- Content pane -->
<div css="display: flex; align-items: center; padding-left: 1.5rem lg:4rem; padding-right: 1.5rem lg:4rem; padding-top: 4rem; padding-bottom: 4rem;">
<div css="width: 100%; max-width: 28rem; margin-left: auto; margin-right: auto;">
<?php if (!empty($page['config']['pageTitle'])) : ?>
<h1 css="font-size: 1.875rem; font-weight: 700; letter-spacing: -0.025em; color: #0f172a; margin-bottom: 1.5rem;">
<?php echo $page['config']['pageTitle']; ?>
</h1>
<?php endif; ?>
<?php echo $page['content']; ?>
</div>
</div>
<!-- Visual pane -->
<div css="display: none lg:block; background: #0f172a;" style="<?php echo $paneStyle; ?>"></div>
</div>
Header System
Header files live in headers/ and are loaded by name. The built-in default header is a slim, sticky top bar with:
- Bolt CMS logo and wordmark linking to the site root
- A "Docs" badge link next to the logo
- Mobile hamburger button that toggles the sidebar
- Frosted-glass backdrop blur effect
- Fixed height of 3.5rem (56px)
Footer System
Footer files live in footers/ and follow the same naming pattern. The built-in default footer is intentionally minimal — often empty on documentation pages, where the sidebar and prev/next navigation already give readers enough context.
Creating Custom Headers and Footers
Headers and footers work the same way as layouts — a PHP file loaded by name, with the $page array in scope. This is how you give different sections of a site different navigation or footers. Create a file in headers/ (or footers/) and echo whatever markup you need:
<!-- headers/minimal.php -->
<header css="border-bottom: 1px_solid_#e2e8f0;">
<div css="max-width: 80rem; margin-left: auto; margin-right: auto; padding-left: 1.5rem; padding-right: 1.5rem; height: 3.5rem; display: flex; align-items: center;">
<a href="<?php echo SITEURL; ?>/" css="font-weight: 600;">My Site</a>
</div>
</header>
Select your header or footer per page, exactly like a layout:
$page['config']['header'] = 'minimal';
$page['config']['footer'] = 'minimal';
To render a page with no header or footer at all, set the value to an empty string:
$page['config']['header'] = '';
$page['config']['footer'] = '';