Extend any part of YOOtheme Pro with custom functionalities. For example, add settings sections and panels to the YOOtheme Pro customizer, add elements or content sources to the page builder or load needed asset files into the site.
YOOtheme Pro has a modular and extensible application architecture. Its code is structured into modules. A module is a package of code that extends functionality of YOOtheme Pro.
Custom modules can be added by using a child-theme or a Joomla plugin. The easiest way to quickly add functionality is by using a child theme. This is typically used for client projects. A Joomla plugin should be used when developing a third-party extension for YOOtheme Pro. Take a look at the extensions page to see what is available from the developer community.
Joomla provides a developer guide which explains the basics of creating a plugin. Apart from listening to Joomla events, you can extend and modify the YOOtheme Pro functionality. Use the onAfterInitialise
event to run the code after the application is initialized and use the YOOtheme\Application::load
method to load a custom module.
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Plugin\CMSPlugin;
use YOOtheme\Application;
class plgSystemMyPlugin extends CMSPlugin
{
public function onAfterInitialise()
{
// Check if YOOtheme Pro is loaded
if (!class_exists(Application::class, false)) {
return;
}
// Load a single module from the same directory
$app = Application::getInstance();
$app->load(__DIR__ . '/bootstrap.php');
}
}
The YOOtheme\Application::load
method can also load multiple modules using a glob pattern. In the following example all modules are located in the modules
directory and have their own directory with an entry bootstrap.php
file.
$app->load(__DIR__ . '/modules/*/bootstrap.php');
The easiest way to get started is to try out the example module or take a look at the included YOOtheme Pro modules.
The example module on GitHub demonstrates how to add asset files, builder elements and settings panels. Simply download and unzip the module. The quickest way to try it out is using a child-theme.
YOOtheme Pro modules are a useful resource to get started when creating a custom module. They can be found in the packages
directory in YOOtheme Pro.
At its simplest, a YOOtheme Pro module is a PHP file, typically named bootstrap.php
, which returns an array with a module definition. The module definition is used to provide bootstrapping, routing and other configuration options. Here you can listen to events, add custom classes and your own controllers. In the child theme the equivalent file is the config.php
file.
<?php
return [
// Set theme configuration values
'theme' => [
],
// Register event handlers
'events' => [
]
];
Use the following property keys in the module definition array.
Option | Description |
---|---|
events |
Register event handlers. |
theme |
Set theme configuration values. |
routes |
Register routes. |
actions |
Register Joomla event handlers or WordPress action handlers. |
filters |
Register WordPress filter handlers. |
config |
Set application configuration values. |
extend |
Extend service definitions. |
services |
Register services. |
aliases |
Register service aliases. |
Register event handlers and listen to various events that are triggered during the execution of the theme application.
Event | Description |
---|---|
theme.init |
Fires after the theme is initialized |
theme.head |
Fires before the head is rendered |
customizer.init |
Fires before customizer is initialized |
source.init |
Fires before the source schema is initialized |
The order of event handlers is defined by their priority. The default priority is set to 0
. Higher priorities are executed earlier.
include_once __DIR__ . '/src/MyListener.php';
return [
'events' => [
'theme.head' => [
MyListener::class => 'initHead'
],
'theme.init' => [
MyListener::class => ['initTheme', 10]
],
'customizer.init' => [
MyListener::class => ['initCustomizer', -10]
]
]
];
Event handlers are defined in their own listener class.
class MyListener
{
public static function initHead() {}
public static function initTheme() {}
public static function initCustomizer() {}
}
The customizer configuration defines all settings in the customizer. Add new settings by extending it with a custom JSON file. Simply register an event handler SettingsListener::initCustomizer
to the customizer.init
event.
include_once __DIR__ . '/src/SettingsListener.php';
return [
'events' => [
'customizer.init' => [
SettingsListener::class => 'initCustomizer'
]
]
];
Use the YOOtheme\Config
service and its YOOtheme\Config::addFile()
method to add a JSON configuration file to the global configuration of YOOtheme Pro. In this example it's added to the customizer
configuration. The Path::get()
helper method resolves the path relative to the file the method is called from.
use YOOtheme\Config;
use YOOtheme\Path;
class SettingsListener
{
public static function initCustomizer(Config $config)
{
$config->addFile('customizer', Path::get('./customizer.json'));
}
}
Now the customizer.json
is loaded into the customizer configuration. For example, it can be used to add new settings to the customizer or to add custom Less files into the style customizer.
To add custom elements to the page builder, extend the YOOtheme\Builder
service by adding new elements. The YOOtheme\Builder::addTypePath
method adds multiple elements using a glob pattern. In the following example all elements are located in the elements
directory and have their own directory with their element.json
configuration file.
use YOOtheme\Builder;
use YOOtheme\Path;
return [
'extend' => [
Builder::class => function (Builder $builder) {
$builder->addTypePath(Path::get('./elements/*/element.json'));
}
]
];
To add custom asset files to the head of the HTML document, register an event handler AssetsListener::initHead
to the theme.head
event.
include_once __DIR__ . '/src/AssetsListener.php';
return [
'events' => [
'theme.head' => [
AssetsListener::class => 'initHead'
]
]
];
Use the YOOtheme\Metadata
service and its YOOtheme\Metadata::set()
method to add metadata elements like in this case the assets files. The first argument defines the meta data type and after the :
a unique identifier is given. The second argument is either an array of attributes or the content of the HTML element.
use YOOtheme\Metadata;
class AssetsListener
{
public static function initHead(Metadata $metadata)
{
// Style file
$metadata->set('style:my-css', ['href' => Path::get('../assets/css/custom.css')];
// Inline style
$metadata->set('style:my-inline-css', 'body {color: blue}');
// Script file
$metadata->set('script:my-js', 'src' => Path::get('../assets/js/custom.js'), 'defer' => true]);
// Inline script
$metadata->set('script:my-inline-js', 'var custom = 123;');
}
}
To add a custom Less file to the theme
configuration, register a custom component name and the path to the Less file under the styles.components
key. Use the same file basename as the component name.
return [
'theme' => [
'styles' => [
'components' => [
'my-component' => Path::get('./assets/less/my-component.less'),
],
],
],
];
All defined Less variables will appear in the new component panel in the style customizer. Mind that variables prefixed with internal
will not show up in the style customizer.
// Name: My Component
// Description: This is my custom component
//
// Component: `my-component`
//
// Sub-objects: `my-component-title`
// `my-component-card`
//
// ========================================================================
// Variables
// ========================================================================
@my-component-title-margin: 40px;
@my-component-title-color: #ddd;
@my-component-card-background: @global-primary-background;
@internal-my-component-card-gradient: ~'';
/* ========================================================================
Component: My Component
========================================================================== */
.my-component-title {
margin-top: @my-component-title-margin;
.hook-my-component-title();
}
.my-component-card {
background-color: @my-component-card-background;
.hook-my-component-card();
}
//
// Following properties will only be set if they differ from their default value
//
// Color
.hook-my-component-title() when not (@my-component-title-color = transparent) {
color: @my-component-title-color;
}
// Background Gradient
// Mind that variables prefixed with `internal` will not show up in the style customizer.
.hook-my-component-card() when not (@internal-my-component-card-gradient = ~'') {
background-image: @internal-my-component-card-gradient;
}
To further integrate the component with the style customizer, extend the module definition by adding a custom JSON file to the style customizer configuration.
use YOOtheme\Theme\Styler\StylerConfig;
return [
'config' => [
StylerConfig::class => __DIR__ . '/config/styler.json',
],
];
The JSON file with the custom component:
{
"components": {
"my-component": {
"name": "My Component",
"groups": {
"title": "@my-component-title-*",
"card": "@my-component-card-*"
},
"hover": "[class*='my-component-']",
"inspect": "[class*='my-component'], [class*='my-component'] > *"
}
}
}
Option | Description |
---|---|
name |
Title of the component panel. |
groups |
Group Less variables by their prefixes. |
hover |
A CSS selector which defines which HTML elements are highlighted with a blue border in the preview to the right when hovering the component menu in the style customizer. |
inspect |
A CSS selector which defines which HTML elements show a tooltip with the component name when hovering them in the preview to the right. |
To trigger a compiling of the Less sources, listen for the YOOtheme\Theme\Styler\StylerConfig
event, and set the property update
in the config to true
. The compilation will happen upon opening the customizer.
use YOOtheme\Theme\Styler\StylerConfig;
include_once __DIR__ . '/src/StyleListener.php';
return [
'events' => [
StylerConfig::class => [StyleListener::class => 'config'],
]
];
The event handler is defined in its own class.
use YOOtheme\Theme\Styler\StylerConfig;
class StyleListener
{
public static function config(StylerConfig $config): StylerConfig
{
if (/* Your conditional code */) {
// Style needs to be re-compiled
$config['update'] = true;
}
return $config;
}
}
Sections are top-level entry points which have their own menu items in the YOOtheme Pro customizer, for example the Layout
and Settings
sections. Typically, a section contains a settings panel with a group of fields, but it can also have a navigation to deeper nested panels. To add a section or a panel to the customizer, first extend the customizer configuration with a custom JSON file.
The JSON configuration defines the sections and panels and how the editing interface should look like inside the builder. Make sure to use unique keys for sections, panels and fields.
Property | Description |
---|---|
title |
Label in the customizer |
width |
Width of the customizer sidebar when editing the element |
priority |
Define where the section is shown in the customizer navigation. |
fields |
Define the fields of the section. |
The following example adds a My Section menu item to YOOtheme Pro which opens a simple section configuration with a field.
{
"sections": {
"my-section": {
"title": "My Section",
"width": 400,
"priority": 100,
"fields": {
"option_a": {
"label": "Option A",
"description": "A description text."
}
}
}
}
}
The following example adds a My Panel menu item to the Settings section in the customizer which opens a simple panel configuration with a field.
{
"sections": {
"settings": {
"fields": {
"settings": {
"items": {
"my-panel": "My Panel"
}
}
}
}
},
"panels": {
"my-panel": {
"title": "My Panel",
"width": 400,
"fields": {
"option_b": {
"label": "Option B",
"description": "A description text."
}
}
}
}
}
The configuration of the Layout section and all its panels is a useful resource to get started when creating new sections and panels. It can be found in the respective module directory under packages/theme/config/customizer.json
in YOOtheme Pro. Learn more about fields and their types in the custom element documentation.
If a dynamic configuration is needed, use the YOOtheme\Config
service to directly modify the configuration instead of using a static JSON file.
The following example adds a My Section menu item to YOOtheme Pro which opens a simple section configuration with a field.
use YOOtheme\Config;
class SettingsListener
{
public static function initCustomizer(Config $config)
{
$config->set('customizer.sections.my-section', [
'title' => 'My Section',
'width' => 400,
'priority' => 100,
'fields' => [
'option_a' => [
'label' => 'Option A',
'description' => 'A description text.'
]
]
]);
}
}
The following example adds a My Panel menu item to the Settings section in the customizer which opens a simple panel configuration with a field.
use YOOtheme\Config;
class SettingsListener
{
public static function initCustomizer(Config $config)
{
$config->set('customizer.panels.my-panel', [
'title' => 'My Panel',
'width' => 400,
'fields' => [
'option_a' => [
'label' => 'Option A',
'description' => 'A description text.'
]
]
]);
$config->set('customizer.sections.settings.fields.settings.items.my-panel', 'My Panel');
}
}
All fields added to the customizer are stored under the ~theme
namespace and can be accessed in the template files.
echo $config('~theme.option_a');
If the $config
variable is not available already, the YOOtheme\Config
service can be accessed using the global \YOOtheme\app()
function.
namespace YOOtheme;
$config = app(Config::class);
echo $config('~theme.option_a');
Mind that field names have to be unique. Therefore, it's recommended to add a namespace to the field names using the dot notation.
"fields": {
"my_namespace.option_a": {
"label": "Option A"
},
"my_namespace.option_b": {
"label": "Option B"
}
}
Use the YOOtheme\Config::addAlias()
method to define an alias and shorten the call to access the field value.
$config->addAlias('~my_namespace', '~theme.my_namespace');
echo $config('~my_namespace.option_a');
echo $config('~my_namespace.option_b');