1
Theme development / Theme Developers Guide for ElkArte 2.0.x
Last post by Spuds -Overview
This guide shows theme developers how to customize ElkArte's new modular theme system. The theme system is now organized into logical groups that are easier to understand and modify.
Quote Think of this like CSS overrides - you can customize specific parts without breaking the whole theme. Each section below handles a different aspect of your theme.
Understanding the New Structure
ElkArte themes are now organized into these main areas:
-
Template Rendering - How pages are displayed (header, footer, layout)
-
Context Management - User information and theme data
-
Feature Integration - Special features (videos, code highlighting, etc.)
-
Asset Management - CSS, JavaScript, and theme variants
-
Headers Management - Browser communication settings
Getting Started: Your Theme File
Your theme file is located at: themes/your_theme_name/Theme.php
Here's the basic structure you'll work with:
Code: [Select]
<?php
namespace ElkArte\Themes\YourThemeName;
use ElkArte\Themes\Theme as BaseTheme;
class Theme extends BaseTheme
{
public function getSettings()
{
return [
// Your theme settings here
'theme_version' => '2.0',
'theme_variants' => ['light', 'dark'],
// ... more settings
];
}
// Add your custom functions here
}
What This Controls
- How the page header and footer are displayed
- Loading of template layers
- Admin warnings and notices
- Copyright display
Common Customization's
Adding Custom CSS to Every Page
Code: [Select]
public function template_header(): void
{
// First, do the normal header stuff
parent::template_header();
// Now add your custom CSS
global $context;
$context['html_headers'] .= '
<link rel="stylesheet" href="' . $settings['theme_url'] . '/css/my_custom.css">
<style>
.my_custom_class {
background-color: #your_color;
}
</style>';
}
Adding Custom JavaScript to Every Page
Code: [Select]
public function template_header(): void
{
parent::template_header();
// Add your custom JavaScript
$this->addInlineJavascript('
document.addEventListener("DOMContentLoaded", function() {
console.log("My custom theme is loaded!");
// Your custom JavaScript code here
});
');
}
Customizing the Footer
Code: [Select]
public function template_footer(): void
{
// Add custom content before the normal footer
echo '<div class="my_custom_footer_content">
<p>Powered by My Amazing Theme</p>
</div>';
// Then show the normal footer
parent::template_footer();
}
Custom Copyright Message
Code: [Select]
public function theme_copyright(): void
{
global $forum_copyright;
// Replace the default copyright with your own
$forum_copyright = 'My Custom Forum © 2026 | Powered by ElkArte';
echo '<span class="my_copyright">' . $forum_copyright . '</span>';
}
Customizing User Context
What This Controls
- User information display
- Guest vs logged-in user handling
- Forum statistics
- News display
- Page titles and metadata
Common Customizations
Adding Custom User Information
Code: [Select]
public function setupLoggedUserContext(): void
{
global $context;
// Do the normal user setup first
parent::setupLoggedUserContext();
// Add your custom user information
$context['user']['custom_title'] = 'VIP Member'; // Example
$context['user']['theme_preference'] = 'dark'; // Example
}
Customizing Page Titles
Code: [Select]
public function setContextThemeData(): void
{
global $context, $mbname;
// Do normal setup first
parent::setContextThemeData();
// Customize the page title format
$context['page_title'] = $context['page_title'] . ' | ' . $mbname . ' - Your Custom Tagline';
// Add custom meta tags
$context['html_headers'] .= '
<meta name="description" content="Your custom forum description">
<meta name="keywords" content="forum, community, your, keywords">';
}
Adding Custom News Display
Code: [Select]
public function setupNewsLines(): void
{
global $context;
// Do the normal news setup
parent::setupNewsLines();
// Add your custom news processing
if (!empty($context['news_lines'])) {
foreach ($context['news_lines'] as $key => $news) {
// Add custom styling to each news item
$context['news_lines'][$key] = '<span class="my_news_style">' . $news . '</span>';
}
}
}
Customizing Special Features
What This Controls
- Video embedding appearance
- Code highlighting themes
- Relative time display
- Scheduled tasks
Common Customization's
Customizing Video Embedding
Code: [Select]
public function autoEmbedVideo(): void
{
global $txt, $modSettings;
if (!empty($modSettings['enableVideoEmbeding'])) {
// Load the video embedding script
loadJavascriptFile('elk_jquery_embed.js', ['defer' => true]);
// Customize the video embedding settings
$this->addInlineJavascript('
if (typeof oEmbedtext === "undefined") {
var oEmbedtext = ({
embed_limit : 10, // Limit to 10 videos per page
preview_image : "▶️ Click to play video",
// Add your custom text
youtube : "YouTube Video",
vimeo : "Vimeo Video",
});
document.addEventListener("DOMContentLoaded", () => {
if ($.isFunction($.fn.linkifyvideo)) {
$().linkifyvideo(oEmbedtext);
}
});
}
', true);
}
}
Adding Custom Code Highlighting
Code: [Select]
public function addCodePrettify(): void
{
global $modSettings;
if (!empty($modSettings['enableCodePrettify'])) {
// Load your custom prettify theme
$this->loadVariant('prettify');
loadJavascriptFile('ext/prettify.min.js', ['defer' => true]);
// Add custom styling
loadCSSFile('my_code_theme.css');
$this->addInlineJavascript('
document.addEventListener("DOMContentLoaded", () => {
if (typeof prettyPrint === "function") {
prettyPrint();
// Add custom code block styling
document.querySelectorAll("pre").forEach(function(block) {
block.classList.add("my-custom-code-style");
});
}
});
', true);
}
}
Customizing Assets (CSS/JS)
What This Controls
- Loading CSS and JavaScript files
- Theme variants (light/dark modes)
- Custom styling
- Progressive Web App features
Common Customizations
Adding Theme-Specific CSS
Create a method that loads additional CSS for your theme:
Code: [Select]
public function loadThemeJavascript(): void
{
// Do the normal JavaScript loading
parent::loadThemeJavascript();
// Add your theme-specific files
loadCSSFile('my_theme_extras.css');
loadJavascriptFile('my_theme_extras.js', ['defer' => true]);
// Add theme-specific JavaScript variables
$this->addJavascriptVar([
'my_theme_color' => '#your_primary_color',
'my_theme_name' => 'My Awesome Theme'
]);
}
Creating Custom Theme Variants
First, create variant directories in your theme:
Code: [Select]
themes/your_theme/css/
├── _light/
│ ├── index_light.css
│ └── custom_light.css
├── _dark/
│ ├── index_dark.css
│ └── custom_dark.css
Then customize the variant loading:
Code: [Select]
public function loadThemeVariant(): void
{
global $context, $settings;
// Do normal variant loading
parent::loadThemeVariant();
// Add custom variant-specific CSS
if (!empty($context['theme_variant'])) {
$this->loadVariant('my_custom_styles', true);
// Add variant-specific JavaScript
$this->addInlineJavascript('
document.body.classList.add("theme-variant' . $context['theme_variant'] . '");
');
}
}
Advanced Customization's
Adding Custom Settings to getSettings()
Code: [Select]
public function getSettings()
{
// Get the default settings first
$settings = parent::getSettings();
// Add your custom settings
return array_merge($settings, [
// Your color scheme
'primary_color' => '#3498db',
'secondary_color' => '#2c3e50',
// Custom theme variants
'theme_variants' => ['light', 'dark', 'purple', 'green'],
// Custom avatar settings
'avatars_on_indexes' => 3, // Show both first and last post avatars
// Custom page index styling
'page_index_template' => [
'base_link' => '<li class="my-page-btn"><a href="{base_link}">%2$s</a></li>',
'current_page' => '<li class="my-current-page"><strong>%1$s</strong></li>',
],
// Enable custom features
'my_theme_features' => [
'custom_header' => true,
'animated_buttons' => true,
'gradient_backgrounds' => true,
]
]);
}
Complete Example: Custom Theme Class
Here's a complete example showing how to create a custom theme:
Code: [Select]
<?php
namespace ElkArte\Themes\MyAwesomeTheme;
use ElkArte\Themes\Theme as BaseTheme;
class Theme extends BaseTheme
{
public function getSettings()
{
return [
'theme_version' => '2.0',
'theme_variants' => ['light', 'dark', 'colorful'],
'primary_color' => '#e74c3c',
'require_theme_strings' => false,
'avatars_on_indexes' => 1,
];
}
public function template_header(): void
{
parent::template_header();
// Add custom CSS
global $context, $settings;
$context['html_headers'] .= '
<link rel="stylesheet" href="' . $settings['theme_url'] . '/css/awesome.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">';
// Add theme initialization
$this->addInlineJavascript('
document.addEventListener("DOMContentLoaded", function() {
document.body.classList.add("awesome-theme");
console.log("🎨 Awesome Theme Loaded!");
});
');
}
public function setContextThemeData(): void
{
global $context;
parent::setContextThemeData();
// Add custom meta tags
$context['html_headers'] .= '
<meta name="theme-color" content="#e74c3c">
<meta name="description" content="An awesome forum powered by ElkArte">';
// Customize page title
if (isset($context['page_title'])) {
$context['page_title'] .= ' 🎨';
}
}
public function theme_copyright(): void
{
echo '<div class="awesome-copyright">
<p>Powered by <strong>Awesome Theme</strong> & ElkArte</p>
</div>';
}
}
Tips for Theme Developers
1. Always Call Parent First
Code: [Select]
public function template_header(): void
{
// Good: Call parent first
parent::template_header();
// Then add your customizations
}
2. Use CSS Classes for Styling
Instead of inline styles, add CSS classes:
Code: [Select]
// Avoid inline styles
echo '<div style="color: red;">Content</div>';
// Better: Use CSS classes
echo '<div class="my-theme-highlight">Content</div>';
3. Test Your Customizations
- Test with different user types (guest, member, admin)
- Test with different theme variants
- Test on mobile devices
- Inspect Element to see what's happening
- Check the Console tab for JavaScript errors
- Use the Network tab to see if CSS/JS files are loading
1. Forgetting parent:: calls - This breaks core functionality
2. Not using global variables - Use global $context, $settings; when needed
3. Not testing thoroughly - Test different pages and user types
Remember: You're not replacing the entire system, just customizing specific parts to make your theme unique!