Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,059 of 9,059 pages
About the help in other languages
All help pages

A theme by hand

Build the smallest complete theme file by file - metadata, the design registration, a siteaccess, a pagelayout, a stylesheet and an override - and check each step, so you know what every file of a theme does.

Download as PDF

Address: https://doc.exponential.earth/exphelp/viewer/help/extension-development/part-6-theme-developers/a-theme-by-hand.md

On this page

A theme by hand

Part 6, chapter 4. The wizard of the previous chapter is fast; writing a theme by hand once teaches you what every file is for. This chapter builds mytheme, the smallest theme that changes a whole site, in six files. Each sample was checked: the pagelayout was rendered through the template engine, and the INI sections and settings exist in the kernel's design.ini and site.ini.

The files

extension/mytheme/
  ezinfo.php
  extension.xml
  settings/design.ini.append.php
  settings/siteaccess/mysite/site.ini.append.php
  design/mytheme/templates/pagelayout.tpl
  design/mytheme/stylesheets/mytheme.css

Replace mysite with the name of your public siteaccess.

1. Who you are: ezinfo.php and extension.xml

Write them as ezinfo.php and extension.xml describe: a readable name, version 1.0.0, your copyright, the license, a website.

2. Register the design: settings/design.ini.append.php

<?php /* #?ini charset="utf-8"?

[ExtensionSettings]
DesignExtensions[]=mytheme

[StylesheetSettings]
FrontendCSSFileList[]=mytheme.css

*/ ?>

DesignExtensions[] adds extension/mytheme/design/ to the folders where designs are looked for. FrontendCSSFileList[] names the stylesheet for the pagelayout to load.

3. Use the design: the siteaccess's site.ini

settings/siteaccess/mysite/site.ini.append.php:

<?php /* #?ini charset="utf-8"?

[DesignSettings]
SiteDesign=mytheme
AdditionalSiteDesignList[]
AdditionalSiteDesignList[]=base

*/ ?>

SiteDesign is searched first; then the designs of AdditionalSiteDesignList[]; the kernel's StandardDesign (standard) last. The empty AdditionalSiteDesignList[] line clears what the siteaccess had before, so the list is exactly what you wrote. More in Themes and siteaccesses.

4. The frame: design/mytheme/templates/pagelayout.tpl

<!DOCTYPE html>
<html lang="{ezini( 'RegionalSettings', 'ContentObjectLocale' )|extract_left( 2 )|wash}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{ezini( 'SiteSettings', 'SiteName' )|wash}</title>
{ezcss_load( ezini( 'StylesheetSettings', 'FrontendCSSFileList', 'design.ini' ) )}
</head>
<body class="mytheme">
<header class="mytheme-header">
  <a class="mytheme-logo" href={'/'|ezurl}>{ezini( 'SiteSettings', 'SiteName' )|wash}</a>
</header>
<main id="content" class="mytheme-content">
{$module_result.content}
</main>
<footer class="mytheme-footer">
  <p>{currentdate()|datetime( 'custom', '%Y' )} {ezini( 'SiteSettings', 'SiteName' )|wash}</p>
</footer>
{ezscript_load( array( 'ezjsc::jquery', ezini( 'JavaScriptSettings', 'FrontendJavaScriptList', 'design.ini' ) ) )}
</body>
</html>

What each part does:

LinePurpose
ezini( 'RegionalSettings', 'ContentObjectLocale' )the site's locale (for example eng-US); extract_left( 2 ) gives the language for lang
ezcss_load( ... )ezjscore's function: bundles the listed stylesheets into one file and prints its <link>
{'/'|ezurl}the address of the site's front page, already quoted
$module_result.contentthe output of the module that answered the request: the page's main content
ezscript_load( ... )bundles and prints the scripts, here jQuery from ezjscore and your frontend list

Every value printed into HTML goes through wash.

Note

Rendered outside a real request, this pagelayout prints everything but the content, and the template engine reports that $module_result is unknown. In a page request the kernel sets it.

5. The look: design/mytheme/stylesheets/mytheme.css

:root {
    --mytheme-ink: #1f2328;
    --mytheme-surface: #ffffff;
    --mytheme-accent: #0b6bcb;
    --mytheme-space: 1rem;
}
body.mytheme { margin: 0; color: var(--mytheme-ink); background: var(--mytheme-surface); font-family: system-ui, sans-serif; }
.mytheme-header, .mytheme-footer { padding: var(--mytheme-space); }
.mytheme-content { max-width: 60rem; margin: 0 auto; padding: var(--mytheme-space); }
.mytheme-logo { color: var(--mytheme-accent); font-weight: 700; text-decoration: none; }

Variables first: the chapter Dark mode and design tokens in themes builds on them.

6. Switch it on and look

./console exp:ini add 'site.ini/ExtensionSettings/ActiveExtensions[]' mytheme global
php bin/php/ezcache.php --clear-all

Open the site: your header, the content as before, your footer, your colors. Every content page still uses the templates of base and standard inside your frame. From here, replace them one at a time (A theme with the RAD wizard, section "From skeleton to theme").

Checking which template was used

Turn on template debugging for your development siteaccess (site.ini [TemplateSettings] ShowUsedTemplates=enabled with the debug output on). The debug output then lists every template the page used and the design it came from (Debugging templates).

Become an Exponential Partner

Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x