Simple Website Framework

The Theme System

Monday April 20th, 2026

Written by Scary le Poo

The framework uses a simple file-based theme system. Each theme is a folder inside themes/ containing a set of PHP files that control the site's HTML structure and page layouts. The active theme is set by the $theme variable in config/config.php.

The repository ships with one theme: themes/skeleton/. This is intended as your starting point — clone it, rename the folder, point $theme at it, and customise from there.


Theme File Structure

A complete theme folder contains the following files:

themes/your-theme/
├── header.php              # <head>, navigation, hero banner — opened but not closed
├── footer.php              # Closing tags, footer columns, below-content scripts
├── navigation.php          # The rendered navigation bar HTML
├── navigation-options.php  # StellarNav JavaScript initialisation
├── navigation.css          # Navigation bar styles
├── custom.css              # Your theme's main stylesheet
├── page-md.php             # Layout: Markdown page with title
├── page-md-notitle.php     # Layout: Markdown page without title
├── page-html.php           # Layout: HTML page with title
├── page-html-notitle.php   # Layout: HTML page without title
├── postarchives.php        # Layout: Post archive/listing page
├── postarchives-styled.php # Layout: Styled post archive variant
└── postarchives-notitle.php # Layout: Post archive without title

How a Page is Assembled

When a request comes in, index.php determines the page name, extracts metadata from the page file, then hands off to the theme's header.php. The header file is responsible for pulling in everything else via PHP includes. The overall assembly order is:

  1. themes/[theme]/header.php — outputs , loads CSS and plugins, renders the nav bar and hero, then includes the layout file based on $pagelayout
  2. The layout file (e.g. page-md.php) — reads the page content file from pages/, processes shortcodes, renders Markdown (if applicable), and outputs the content
  3. themes/[theme]/footer.php — outputs footer columns, flushes $pluginCalledBelowContent, includes navigation-options.php, and closes

Available Variables in Theme Files

The following PHP variables are available inside all theme files:

Variable Type Description
$pagename string The URL slug of the current page (e.g. documentation/introduction)
$pagetitle string From page metadata. Falls back to ucwords($pagename) if not set
$pagelayout string The layout filename to use (without .php)
$pagedate string Publication date, normalised to Y-m-d H:i:s. Falls back to file modification time
$pageimage string Relative path to the page image. Falls back to $WebsiteImage
$pageexcerpt string Short description. Falls back to $WebsiteDescription
$pagekeywords string SEO keywords. Falls back to $WebsiteKeywords
$pageauthor string Author name. Falls back to $WebsiteAuthor
$pagetype string OpenGraph type (e.g. article, website). Falls back to "website"
$WebsiteTitle string From config
$WebsiteURL string Full canonical URL (protocol + domain)
$currentURL string Full URL of the current request
$theme string Active theme name
$showhomepagetitle bool Whether to show title on home page
$pluginCalledBelowContent string Accumulated plugin scripts, flushed in footer

Page Layouts

The $pagelayout metadata value in each page file controls which layout PHP file is used to render the content. If pagelayout is not set, the framework defaults to page-md.

Layout value File Behaviour
page-md page-md.php Renders page content as Markdown, shows title
page-md-notitle page-md-notitle.php Renders page content as Markdown, hides title
page-html page-html.php Renders page content as raw HTML, shows title
page-html-notitle page-html-notitle.php Renders page content as raw HTML, hides title
postarchives postarchives.php Generates a paginated list of all posts
postarchives-styled postarchives-styled.php Styled variant of the post archive
postarchives-notitle postarchives-notitle.php Post archive without a page title

You can add your own layout files to the theme folder and reference them by name in page metadata.


The Navigation File

Navigation is defined in pages/navigation.html as a standard Markdown unordered list. The navigation.php theme file reads this file, passes it through the Markdown parser, and outputs it inside a <div class="stellarnav"> wrapper which StellarNav then enhances.

Nested lists become dropdown menus. Use #0 as the href for parent items that should not be links themselves:

* Home
* [About](#0)
  * [Team](/about/team)
  * [History](/about/history)
* [Contact](/contact)

Nesting can go three levels deep. See the StellarNav page for configuration options.


Footer Columns

Footer content is driven by Markdown files in pages/footer/. Each file corresponds to one column. The number of columns is set by $numberFooterColumns in config.php. Files must be named footer1.html, footer2.html, etc.

Each footer file can optionally include a section title using an HTML comment at the top:

<!-- sectiontitle: About Us -->

Some footer content here.

Creating a New Theme

  1. Copy themes/skeleton/ to a new folder, e.g. themes/mytheme/
  2. Update $theme = "mytheme"; in config/config.php
  3. Edit custom.css for your styles and modify the PHP layout files as needed
  4. The required/ files and core CSS (css/base.css, css/flexgridsystem.css, css/normalize.css) are shared across all themes and should not need to be duplicated

Keep the include calls in header.php intact — they load the plugin system, metadata, and navigation. The layout include (include $pagelayout . ".php") and the footer.php include at the bottom of header.php are also required for the framework to function.

Simple Website Framework

A lean, flat-file PHP framework with no database, no bloat, and no black boxes. Build something clean and keep control of every part of it.

Contribute on GitHub

Found a bug? Have an idea? The project is open source and contributions are welcome. Find it on GitHub.

Documentation

This site is the living documentation for the framework. Every feature is demonstrated in the place it's meant to be used. Start with the introduction or dive straight into the setup guide.