The complete guide to converting an HTML template into a JekCMS theme — template hierarchy, partials, template tags, assets, and dark mode support.
Starting Point: The HTML Mockup
Every JekCMS theme starts as a static HTML/CSS mockup. You design your pages in pure HTML, get the layout and styling perfect, then convert to a dynamic PHP theme. This approach is faster than designing in PHP from scratch because you can iterate on the HTML without worrying about database queries or template logic.
A typical HTML mockup has: index.html (homepage), post.html (single post), category.html (category archive), and 404.html (error page). These map directly to JekCMS template files.
Template Hierarchy
themes/mytheme/
├── index.php # Homepage
├── single.php # Single post
├── category.php # Category archive
├── tag.php # Tag archive
├── author.php # Author page
├── search.php # Search results
├── 404.php # Not found
├── templates/
│ ├── partials/
│ │ ├── header.php
│ │ ├── footer.php
│ │ ├── sidebar.php
│ │ ├── post-card.php
│ │ └── pagination.php
│ └── page-contact.php # Custom page template
├── assets/
│ ├── css/style.css
│ ├── js/main.js
│ └── images/
└── theme.json # Theme metadata
Converting HTML to PHP: Header
The HTML <head> section becomes templates/partials/header.php:
<!DOCTYPE html>
<html lang="<?= get_current_language() ?>" data-theme="<?= get_theme_mode() ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Dynamic title -->
<title><?= htmlspecialchars($pageTitle ?? get_setting('general', 'site_name')) ?></title>
<!-- SEO meta tags -->
<?php output_meta_tags(); ?>
<?php output_og_tags(); ?>
<?php output_hreflang_tags(); ?>
<!-- CSS -->
<link rel="stylesheet" href="<?= theme_url('/assets/css/style.css?v=' . CMS_VERSION) ?>">
<!-- Analytics/Scripts -->
<?php if (function_exists('output_head_scripts')) output_head_scripts(); ?>
</head>
Template Tags
JekCMS provides helper functions (template tags) that replace hardcoded HTML content:
get_setting('group', 'key')— Read site settingsget_posts($options)— Query posts with filtersget_categories()— List all categoriesget_featured_image($post, 'medium')— Get thumbnail URLsite_url($path)— Generate absolute URLtheme_url($path)— URL relative to theme directoryformat_date($date, 'j F Y')— Localized date formatting
Post Card Partial
The post card is used on the homepage, category pages, search results, and sidebar. Creating it as a partial means one file to maintain:
<!-- templates/partials/post-card.php -->
<article class="post-card">
<?php $img = get_featured_image($post, 'medium'); if ($img): ?>
<a href="<?= site_url('/blog/' . $post['slug']) ?>" class="card-image">
<img src="<?= $img ?>" alt="<?= htmlspecialchars($post['title']) ?>"
width="800" height="500" loading="lazy">
</a>
<?php endif; ?>
<div class="card-content">
<span class="card-category"><?= htmlspecialchars($post['category_name'] ?? '') ?></span>
<h2><a href="<?= site_url('/blog/' . $post['slug']) ?>">
<?= htmlspecialchars($post['title']) ?>
</a></h2>
<p><?= htmlspecialchars($post['excerpt'] ?? substr(strip_tags($post['content']), 0, 160)) ?></p>
<time datetime="<?= $post['published_at'] ?>"><?= format_date($post['published_at']) ?></time>
</div>
</article>
Dark Mode Support
Use CSS custom properties for all colors. Define light mode as default and dark mode in a [data-theme="dark"] selector. The theme toggle script saves preference to localStorage and applies it before page render to avoid flash.
Testing Your Theme
Before submitting a theme: run Lighthouse audit (target 90+ on all metrics), test keyboard navigation, verify all template tags output correct data, check responsive design at 375px/768px/1024px/1440px, and verify dark mode switches correctly.