Module 4: Theme Development

Styling and User Experience

1. Theme Structure

Themes reside in the /theme directory. Most modern themes inherit from boost (Bootstrap 4/5 based).

// config.php
$THEME->name = 'mytheme';
$THEME->parents = ['boost']; // Inherit from Boost
$THEME->sheets = ['custom']; // Load custom.css
$THEME->enable_dock = false;

2. Mustache Templates

Moodle uses Mustache for templating. Override core templates by placing them in /theme/mytheme/templates/.

{{! theme/mytheme/templates/core/frontpage.mustache }}
<div class="my-custom-frontpage">
    <h1>{{sitename}}</h1>
    {{{ output.main_content }}}
</div>

3. SCSS Compilation

Moodle compiles SCSS on the fly. Define your SCSS variables and imports in /theme/mytheme/scss/.

4. Renderers

Renderers are PHP classes that generate HTML. You can override any core renderer in your theme.

// theme/mytheme/renderers.php
class theme_mytheme_core_renderer extends core_renderer {
    public function login_info() {
        return '<div class="custom-login">Login Here</div>';
    }
}

5. AMD Modules (JavaScript)

Moodle uses RequireJS (AMD) for modular JavaScript. Place your JS in amd/src/.

// amd/src/myscript.js
define(['jquery', 'core/modal'], function($, Modal) {
    return {
        init: function() {
            $('button').click(function() {
                Modal.create({ title: 'Hello', body: 'World' });
            });
        }
    };
});

Call it from PHP:

$PAGE->requires->js_call_amd('theme_mytheme/myscript', 'init');

6. Theme Settings

Themes can have their own settings page in Site Administration. Define them in settings.php.

// theme/mytheme/settings.php
defined('MOODLE_INTERNAL') || die;

if ($ADMIN->fulltree) {
    $settings = new theme_boost_admin_settingspage_tabs('themesettingmytheme', get_string('configtitle', 'theme_mytheme'));
    $page = new admin_settingpage('theme_mytheme_general', get_string('generalsettings', 'theme_mytheme'));

    // Add a logo upload setting
    $name = 'theme_mytheme/logo';
    $title = get_string('logo', 'theme_mytheme');
    $description = get_string('logodesc', 'theme_mytheme');
    $setting = new admin_setting_configstoredfile($name, $title, $description, 'logo');
    $setting->set_updatedcallback('theme_reset_all_caches');
    $page->add($setting);

    $settings->add($page);
    $ADMIN->add('themes', $settings);
}

7. Layout Files

Layout files define the HTML structure of the page (header, footer, blocks). They are located in theme/mytheme/layout/.

Common layouts include:

// theme/mytheme/layout/columns2.php
echo $OUTPUT->doctype();
?>
<html htmlattributes(); ?>>
<head>
    standard_head_html(); ?>
</head>
<body body_attributes(); ?>>
    standard_top_of_body_html(); ?>
    
    <header>render_from_template('theme_mytheme/navbar', []); ?></header>

    <div id="page" class="container-fluid">
        <div id="page-content" class="row">
            <div id="region-main-box" class="col-12">
                main_content(); ?>
            </div>
        </div>
    </div>

    standard_end_of_body_html(); ?>
</body>
</html>

8. Core JavaScript Libraries

Moodle provides powerful core AMD modules to handle common tasks.

AJAX Calls

define(['core/ajax', 'core/notification'], function(Ajax, Notification) {
    var promises = Ajax.call([{
        methodname: 'local_myplugin_get_items',
        args: { courseid: 123 }
    }]);

    promises[0].done(function(response) {
        console.log(response);
    }).fail(Notification.exception);
});

Language Strings in JS

define(['core/str'], function(Str) {
    Str.get_strings([
        { key: 'yes', component: 'core' },
        { key: 'no', component: 'core' }
    ]).done(function(strings) {
        console.log(strings[0]); // "Yes"
    });
});