Atomic theme runtime is implemented by Engine\Atomic\Theme\Theme.
use Engine\Atomic\Theme\Theme;
Theme::instance(); // first call uses THEME.envname or "default"
Theme::instance('marketing'); // replaces the current singleton and boots that theme
Theme::reset(); // clears the singletonAutomatic default theme boot: Controller::beforeroute() calls Theme::instance() implicitly. Every controller gets the default theme (THEME.envname) without explicit initialization. Controllers that need a specific theme (e.g., ErrorPages, Telemetry) call Theme::instance('name') in their constructor — this replaces the singleton and boots the named theme. Theme::instance() with no arguments on subsequent calls returns the existing singleton without reinitializing.
Runtime behavior:
- Reads the custom-theme root from
ENQ_UI_FIX. - Resolves the theme name from the explicit argument or
THEME.envname. - Uses
<ENQ_UI_FIX>/<theme_name>/when that custom theme containstheme.json. - Otherwise falls back to the bundled theme in
engine/Atomic/Theme/Builtin/<theme_name>/. - Sets
UIto the resolved theme directory and logs an error if neither location exists. - Loads
functions.atom.phpthroughTheme::include(...). - Parses
theme.json. - Publishes theme metadata into the hive as
THEME.*.
Notes:
- A no-argument
Theme::instance()reuses the existing singleton once one has been created. - To switch themes after boot, call
Theme::instance('name')orTheme::reset()first.
Recommended layout:
public/themes/
default/
functions.atom.php
theme.json
partials/
head.atom.php
head.custom.atom.php
header.atom.php
footer.atom.php
sidebar.atom.php
Only functions.atom.php and theme.json are loaded directly by Theme. Partials are rendered later through View.
If theme.json exists and contains valid JSON, its keys are exposed as THEME.<key>.
Bundled themes are framework-internal and are never exposed as source files. Their CSS, JavaScript, fonts, and other static files are served through /__atomic/themes/<theme_name>/.... Custom themes remain public and use the configured public theme path. A custom theme with the same name overrides its bundled counterpart when it contains at least theme.json; an empty legacy directory does not.
Built-in metadata added by Theme::parse():
THEME._file: absolute path totheme.jsonTHEME._dir: active theme directoryTHEME._url: themes-root URL (for example,<public_url>themes/); this remains the public custom-theme root even when the active theme is bundledTHEME._theme: active theme nameTHEME._url_public: public base URL fromMethods::get_public_url()THEME._dir_public: public base directory fromMethods::get_public_dir()
Example:
{
"title": "Default Theme",
"author": "Atomic Team",
"version": "1.2.0",
"color": "#f7f7f7"
}Becomes:
THEME.titleTHEME.authorTHEME.versionTHEME.color
If theme.json is missing, unreadable, or invalid, boot continues and a warning is logged.
Theme methods render exact partial paths under partials/:
Theme::get_head();
Theme::get_custom_head();
Theme::get_header();
Theme::get_footer();
Theme::get_sidebar();
Theme::get_section('hero', ['title' => 'Atomic']);Resolution:
get_head()->partials/head.atom.phpget_custom_head()->partials/head.custom.atom.phpif the file existsget_header('header')->partials/header.atom.phpget_footer('footer')->partials/footer.atom.phpget_sidebar('sidebar')->partials/sidebar.atom.phpget_section('hero')->partials/hero.atom.php
Global helpers from helpers.php:
get_head()get_custom_head()get_header()get_footer()get_sidebar()get_section()
Use Theme::include(...) for PHP files that live inside the active theme:
$theme = Theme::instance();
$ok = $theme->include('inc/hooks.php');Behavior:
- Relative paths are resolved inside the active theme directory.
- Absolute paths are allowed only when they still resolve inside the theme directory.
- Escaping the theme root is rejected and logged.
- Only readable regular files are included.
- Files are loaded with
include_once.
$theme = Theme::instance();
$meta = $theme->get_theme_meta();
$themeDir = $theme->get_theme_dir();
$themeUrl = $theme->get_theme_url();
$themeName = $theme->get_theme_name();
$publicUrl = $theme->get_public_url();
$publicDir = $theme->get_public_dir();Getter details:
get_theme_dir()returns the active theme directory with a trailing directory separator.get_theme_url()returns the active theme asset URL without an added trailing slash. UnlikeTHEME._url, it follows the resolved theme: bundled themes use/__atomic/themes/<theme_name>, while custom themes use<THEME._url><theme_name>.is_builtin()reports whether the active theme came from the framework bundle.get_public_url()andget_public_dir()are the generic public paths, not theme-specific paths.
Color helpers:
get_theme_color()returnstheme.jsoncolorwhen present, otherwise#ffffff.set_theme_color($fallback)returnsPAGE.colorwhen it exists, otherwise the provided fallback.
- Put custom themes under the configured public theme root (the skeleton default is
public/themes/). - Put shared theme bootstrap code in
functions.atom.php. - Keep
theme.jsonfor metadata only; it is copied into the hive unchanged except for the built-inTHEME._*keys. - Use
Theme::instance('name')when a controller must force a specific theme. - Use
Theme::include(...)instead of raw includes for theme-local PHP files. - Keep partial names aligned with the helper that renders them.