Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Creating a theme based on classic: Difference between revisions

From MoodleDocs
(Created page with "{{Moodle 3.6}} {{Template:Themes}} This is a tutorial for how to create a new theme based on the Classic theme. Moodle 3.7 includes a new core theme named "Classic" which is...")
 
m (Text replacement - "<code (.*)>" to "<syntaxhighlight lang="$1">")
 
(13 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{Moodle 3.6}}
{{Moodle 3.7}}
{{Template:Themes}}
{{Template:Themes}}


Line 9: Line 9:
What is a theme? A theme in Moodle is just another type of plugin that can be developed. Themes are responsible for setting up the structure of each page and have the ability to customise the output of any page in Moodle.
What is a theme? A theme in Moodle is just another type of plugin that can be developed. Themes are responsible for setting up the structure of each page and have the ability to customise the output of any page in Moodle.


This tutorial is based on a the tutorial [Creating_a_theme_based_on_boost Creating a theme based on boost].
This tutorial is based on the tutorial https://docs.moodle.org/dev/Creating_a_theme_based_on_boost. You can download it or view the source code for it here: https://github.com/bmbrands/theme_picture


=== Choosing a name ===
=== Choosing a name ===
Your new theme will need a name. Try and think of something short and memorable - and make sure it is not a name that has already been used by someone else. A quick search on the moodle.org/plugins can save you a lot of work renaming things later.
Your new theme will need a name. Try and think of something short and memorable - and make sure it is not a name that has already been used by someone else. A quick search on the [https://moodle.org/plugins moodle.org/plugins] can save you a lot of work renaming things later.


Lets call our new example theme "brands" as we will add some settings to allow "branding" in various places in Moodle.
Let's call our new example theme "picture" as we will add some settings to allow "pictures" in various places in Moodle.


=== Starting files ===
=== Starting files ===
As a plugin, themes must start with the basic structure of a plugin in Moodle. See https://docs.moodle.org/dev/Tutorial#The_skeleton_of_your_plugin for an overview of the files common to all plugins in Moodle.
As a plugin, themes must start with the basic structure of a plugin in Moodle. See https://docs.moodle.org/dev/Tutorial#The_skeleton_of_your_plugin for an overview of the files common to all plugins in Moodle.


Following this guide we can start creating our theme. First we create the folder for the new theme under under "/theme/" folder in the Moodle root directory.
Following this guide you can start creating your own theme. First, we create the folder for the new theme under under "/theme/" folder in the Moodle root directory.


<code>
<syntaxhighlight lang="php">
/theme/picture/
/theme/picture/
</code>
</syntaxhighlight>


Now we need to add some standard plugin files to our theme. First is version.php
Now we need to add some standard plugin files to our theme. First is version.php
Line 29: Line 29:
''/theme/picture/version.php''
''/theme/picture/version.php''


<code php>
<syntaxhighlight lang="php">
<?php
<?php
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real.
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real.
Line 56: Line 56:
// This is the named version.
// This is the named version.
$plugin->release = 1.0;
$plugin->release = 1.0;
</code>
</syntaxhighlight>


We also need a language file so that all our strings can be translated into different languages. The name of this file is the component name of our plugin and it sits in the lang/en/ folder for our plugin. We can include translations of our plugin, but we can also provide translations via the https://lang.moodle.org/ website once our plugin has been published to the plugins database at http://www.moodle.org/plugins/.
We also need a language file so that all our strings can be translated into different languages. The name of this file is the component name of our plugin and it sits in the lang/en/ folder for our plugin. We can include translations of our plugin, but we can also provide translations via the https://lang.moodle.org/ website once our plugin has been published to the plugins database at http://www.moodle.org/plugins/.
Line 62: Line 62:
''/theme/picture/lang/en/theme_picture.php''
''/theme/picture/lang/en/theme_picture.php''


<code php>
<syntaxhighlight lang="php"><?php
 
<?php
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real.
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real.


Line 77: Line 75:
$string['region-side-pre'] = 'Left';
$string['region-side-pre'] = 'Left';
$string['region-side-post'] = 'Right';
$string['region-side-post'] = 'Right';
</code>
</syntaxhighlight>


=== Theme specific files ===
=== Theme specific files ===
Line 97: Line 95:


''lib.php''
''lib.php''
<code php>
<syntaxhighlight lang="php">
<?php
<?php


Line 106: Line 104:


// We will add callbacks here as we add features to our theme.
// We will add callbacks here as we add features to our theme.
</code>
</syntaxhighlight>


Theme config goes in a config.php file. This is one of the most important files in our theme. Once we add this file we will be ready to test our theme for the first time.
Theme config goes in a config.php file. This is one of the most important files in our theme. Once we add this file we will be ready to test our theme for the first time.
Line 112: Line 110:
''config.php''
''config.php''


<code php>
<syntaxhighlight lang="php">
<?php
<?php
// This file is part of Moodle - http://moodle.org/
// This file is part of Moodle - http://moodle.org/
Line 157: Line 155:


// This is a critical setting. We want to inherit from theme_classic because it provides a great starting point for SCSS bootstrap4
// This is a critical setting. We want to inherit from theme_classic because it provides a great starting point for SCSS bootstrap4
// themes. We could add more than one parent here to inherit from multiple parents, and if we did they would be processed in
// themes. We have added add more than one parent here to inherit from multiple parents, and if we did they would be processed in
// order of importance (later themes overriding earlier ones). Things we will inherit from the parent theme include
// order of importance (later themes overriding earlier ones). Things we will inherit from the parent theme include
// styles and mustache templates and some (not all) settings.
// styles and mustache templates and some (not all) settings.
$THEME->parents = ['classic'];
$THEME->parents = ['boost', 'classic'];


// A dock is a way to take blocks out of the page and put them in a persistent floating area on the side of the page.
// A dock is a way to take blocks out of the page and put them in a persistent floating area on the side of the page.
Line 173: Line 171:
$THEME->rendererfactory = 'theme_overridden_renderer_factory';
$THEME->rendererfactory = 'theme_overridden_renderer_factory';


// This is a list of blocks that are required to exist on all pages for this theme to function correctly. For example
$THEME->prescsscallback = 'theme_picture_get_pre_scss';
// bootstrap base requires the settings and navigation blocks because otherwise there would be no way to navigate to all the
 
// pages in Moodle. Classic does not require these blocks because it provides other ways to navigate built into the theme.
// Since we are using 2 parent themes the correct location of the layout files needs to be defined. For this theme we need the multiple
$THEME->requiredblocks = '';
// column layouts.
$THEME->layouts = [
    // Most backwards compatible layout without the blocks - this is the layout used by default.
    'base' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array(),
    ),
    // Standard layout with blocks, this is recommended for most pages with general information.
    'standard' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
    ),
    // Main course page.
    'course' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
        'options' => array('langmenu' => true),
    ),
    'coursecategory' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // Part of course, typical for modules - default page layout if $cm specified in require_login().
    'incourse' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // The site home page.
    'frontpage' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
        'options' => array('nofullheader' => true),
    ),
    // Server administration scripts.
    'admin' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // My dashboard page.
    'mydashboard' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
        'options' => array('nonavbar' => true, 'langmenu' => true, 'nocontextheader' => true),
    ),
    // My public page.
    'mypublic' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    'login' => array(
        'theme' => 'boost',
        'file' => 'login.php',
        'regions' => array(),
        'options' => array('langmenu' => true),
    ),
 
    // Pages that appear in pop-up windows - no navigation, no blocks, no header.
    'popup' => array(
        'theme' => 'classic',
        'file' => 'contentonly.php',
        'regions' => array(),
        'options' => array('nofooter' => true, 'nonavbar' => true),
    ),
    // No blocks and minimal footer - used for legacy frame layouts only!
    'frametop' => array(
        'theme' => 'classic',
        'file' => 'contentonly.php',
        'regions' => array(),
        'options' => array('nofooter' => true, 'nocoursefooter' => true),
    ),
    // Embeded pages, like iframe/object embeded in moodleform - it needs as much space as possible.
    'embedded' => array(
        'theme' => 'boost',
        'file' => 'embedded.php',
        'regions' => array()
    ),
    // Used during upgrade and install, and for the 'This site is undergoing maintenance' message.
    // This must not have any blocks, links, or API calls that would lead to database or cache interaction.
    // Please be extremely careful if you are modifying this layout.
    'maintenance' => array(
        'theme' => 'boost',
        'file' => 'maintenance.php',
        'regions' => array(),
    ),
    // Should display the content and basic headers only.
    'print' => array(
        'theme' => 'classic',
        'file' => 'contentonly.php',
        'regions' => array(),
        'options' => array('nofooter' => true, 'nonavbar' => false),
    ),
    // The pagelayout used when a redirection is occuring.
    'redirect' => array(
        'theme' => 'boost',
        'file' => 'embedded.php',
        'regions' => array(),
    ),
    // The pagelayout used for reports.
    'report' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // The pagelayout used for safebrowser and securewindow.
    'secure' => array(
        'theme' => 'classic',
        'file' => 'secure.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre'
    )
];
</syntaxhighlight>
 
=== Ready set go! ===
If you have been following along - now we are at the point where we can actually install and test our new theme. Try it now by visiting the admin notifications page to install the new plugin, and then choosing the new theme from the theme selector.
 
[[https://docs.moodle.org/en/Standard_themes#Theme_selector Theme selector]]
 
When you choose the new theme - you will find that it looks exactly the same as Classic. At this point with our minimal configuration - we are inheriting almost everything from our parent theme including styles and templates. You will notice though that we don't inherit the settings from our parent theme.


// This is the function that returns the SCSS source for the main file in our theme. We override the boost version because
=== Setup your Sass files ===
// we want to allow presets uploaded to our own theme file area to be selected in the preset list.
Once you have a theme that has been installed into your Moodle setup it is time to start styling it. The core Boostrap scss is very well suited for customization using Sass variables and overriding component. On [https://getbootstrap.com/docs/4.3/getting-started/theming/#variable-defaults Get bootstrap.com] there is some documentation on Sass variables and in theme/boost/scss/boostrap/_variables.scss you will see the full list of variables that can be changed.
 
Before we start the theme needs to be able to return the complete set of Sass files that are required for Moodle and our custom Sass. So as a start we will be editing the config again:
 
''theme/picture/config.php''
<syntaxhighlight lang="php">
// This is the function that returns the SCSS source for the main file in our theme.
$THEME->scss = function($theme) {
$THEME->scss = function($theme) {
     return theme_picture_get_main_scss_content($theme);
     return theme_picture_get_main_scss_content($theme);
};
};
</code>
</syntaxhighlight>


=== Ready set go! ===
The variable $THEME->scss is configured to be a callback function that when called returns all the Scss source files from our parent theme and the picture theme. Once the callback function is defined our Theme's lib.php needs to contain the callback function.
If you have been following along - now we are at the point where we can actually install and test our new theme. Try it now by visiting the admin notifications page to install the new plugin, and then choosing the new theme from the theme selector.
 
''theme/picture/lib.php''
<syntaxhighlight lang="php">
/**
* Returns the main SCSS content.
*
* @param theme_config $theme The theme config object.
* @return string All fixed Sass for this theme.
*/
function theme_picture_get_main_scss_content($theme) {
    global $CFG;
 
    $scss = '';
 
    $fs = get_file_storage();
 
    // Main CSS - Get the CSS from theme Classic.
    $scss .= file_get_contents($CFG->dirroot . '/theme/classic/scss/classic/pre.scss');
    $scss .= file_get_contents($CFG->dirroot . '/theme/classic/scss/preset/default.scss');
    $scss .= file_get_contents($CFG->dirroot . '/theme/classic/scss/classic/post.scss');
 
    // Pre CSS - this is loaded AFTER any prescss from the setting but before the main scss.
    $pre = file_get_contents($CFG->dirroot . '/theme/picture/scss/pre.scss');
 
    // Post CSS - this is loaded AFTER the main scss but before the extra scss from the setting.
    $post = file_get_contents($CFG->dirroot . '/theme/picture/scss/post.scss');
 
    // Combine them together.
    return $pre . "\n" . $scss . "\n" . $post;
}
</syntaxhighlight>
 
The function theme_picture_get_main_scss_content uses the scss files from classic, the default.scss file from classic contains @import statements to include the files from theme Boost so we can be sure we have all the css we need.
 
''theme/classic/scss/preset/default.scss''
<syntaxhighlight lang="css">
// Import FontAwesome.
@import "fontawesome";
 
// Import All of Bootstrap
@import "bootstrap";
 
// Import Core moodle CSS
@import "moodle";
</syntaxhighlight>
 
To complete the setup all we need to do is create the pre.scss and post.scss files that will contain our Sass variables and other Css. The pre.scss file is the file that needs to be loaded first before the files from theme classic are imported. It will contain the variable we configure for things like fonts, colours and spacing. The post.scss file will contain all other custom css.
 
''theme/picture/scss/pre.scss''
<syntaxhighlight lang="css">
// We need larger buttons.
$btn-padding-y:        0.5rem !default;
$btn-padding-x:        1.1rem !default;
</syntaxhighlight>
 
''theme/picture/scss/post.scss''
<syntaxhighlight lang="css">
// We like our buttons very round.
.btn {
  border-radius: 1.078em;
  font-family: $font-family-sans-serif;
  font-size: 0.875em;
}
</syntaxhighlight>
 
You might have noticed the "!default" statement after the variables in our pre.scss file. This means the variable is set if the variable has not been defined before. When inspecting the Bootstrap variables defined in theme/boost/scss/bootstrap/_variables.scss you will see all variable have a "!default" statement meaning we can change any variable!
 
With the preset files in place we can select our theme from the available installed themes (If you did not do that already) and purge changes. You should be able to see the Classic 3 column layout and our round buttons.
 
=== Adding custom settings to our theme ===


[[https://docs.moodle.org/en/Standard_themes#Theme_selector Theme selector]]


When you choose the new theme - you will find that it looks exactly the same as Classic. At this point with our minimal configuration - we are inheriting almost everything from our parent theme including styles and templates. You will notice though that we don't inherit the settings from our parent theme. If you choose a different preset in Classic - it is not applied in this child theme.




Thats it for this tutorial, but there are [[:Category:Themes| more Themes docs]] to browse.
That's it for this tutorial, but there are [[:Category:Themes| more Themes docs]] to browse.




[[Category:Themes]]
[[Category:Themes]]

Latest revision as of 08:16, 15 July 2021

Moodle 3.7


This is a tutorial for how to create a new theme based on the Classic theme.

Moodle 3.7 includes a new core theme named "Classic" which is a starting point for themers wanting to build Moodle theme using a 3 column layout without the Boost navdrawer and settings menus.

Getting started

What is a theme? A theme in Moodle is just another type of plugin that can be developed. Themes are responsible for setting up the structure of each page and have the ability to customise the output of any page in Moodle.

This tutorial is based on the tutorial https://docs.moodle.org/dev/Creating_a_theme_based_on_boost. You can download it or view the source code for it here: https://github.com/bmbrands/theme_picture

Choosing a name

Your new theme will need a name. Try and think of something short and memorable - and make sure it is not a name that has already been used by someone else. A quick search on the moodle.org/plugins can save you a lot of work renaming things later.

Let's call our new example theme "picture" as we will add some settings to allow "pictures" in various places in Moodle.

Starting files

As a plugin, themes must start with the basic structure of a plugin in Moodle. See https://docs.moodle.org/dev/Tutorial#The_skeleton_of_your_plugin for an overview of the files common to all plugins in Moodle.

Following this guide you can start creating your own theme. First, we create the folder for the new theme under under "/theme/" folder in the Moodle root directory.

/theme/picture/

Now we need to add some standard plugin files to our theme. First is version.php

/theme/picture/version.php

<?php
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real.

// This line protects the file from being accessed by a URL directly.
defined('MOODLE_INTERNAL') || die();

// This is the version of the plugin.
$plugin->version = 2019030400;

// This is the version of Moodle this plugin requires.
$plugin->requires = 2018051700;

// This is the component name of the plugin - it always starts with 'theme_'
// for themes and should be the same as the name of the folder.
$plugin->component = 'theme_picture';

// This is a list of plugins, this plugin depends on (and their versions).
$plugin->dependencies = [
    'theme_classic' => 2018120700
];

// This is a stable release.
$plugin->maturity = MATURITY_STABLE;

// This is the named version.
$plugin->release = 1.0;

We also need a language file so that all our strings can be translated into different languages. The name of this file is the component name of our plugin and it sits in the lang/en/ folder for our plugin. We can include translations of our plugin, but we can also provide translations via the https://lang.moodle.org/ website once our plugin has been published to the plugins database at http://www.moodle.org/plugins/.

/theme/picture/lang/en/theme_picture.php

<?php
// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real.

// This line protects the file from being accessed by a URL directly.
defined('MOODLE_INTERNAL') || die();

// A description shown in the admin theme selector.
$string['choosereadme'] = 'Theme picture is a child theme of the Classic. It adds the ability to upload background photos.';
// The name of our plugin.
$string['pluginname'] = 'Picture';
// We need to include a lang string for each block region.
$string['region-side-pre'] = 'Left';
$string['region-side-post'] = 'Right';

Theme specific files

Theme plugins have a few more standard files they need to define.

Themes require a favicon file to show in the address bar. See [Favicon].

pix/favicon.ico

(Image file not shown).

Themes also require an example screenshot to be displayed in the theme selector.

pix/screenshot.jpg

(Image file not shown).

Themes require a lib.php file. This file contains callbacks used by various API's in Moodle. Initially this file can be empty, but as we add features to our theme we will need to add some functions here.

lib.php

<?php

// Every file should have GPL and copyright in the header - we skip it in tutorials but you should not skip it for real.

// This line protects the file from being accessed by a URL directly.
defined('MOODLE_INTERNAL') || die();

// We will add callbacks here as we add features to our theme.

Theme config goes in a config.php file. This is one of the most important files in our theme. Once we add this file we will be ready to test our theme for the first time.

config.php

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Picture config.
 *
 * @package   theme_picture
 * @copyright 2016 Damyon Wiese
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

// This line protects the file from being accessed by a URL directly.
defined('MOODLE_INTERNAL') || die();

// $THEME is defined before this page is included and we can define settings by adding properties to this global object.

// The first setting we need is the name of the theme. This should be the last part of the component name, and the same
// as the directory name for our theme.
$THEME->name = 'picture';

// This setting list the style sheets we want to include in our theme. Because we want to use SCSS instead of CSS - we won't
// list any style sheets. If we did we would list the name of a file in the /styles/ folder for our theme without any css file
// extensions.
$THEME->sheets = [];

// This is a setting that can be used to provide some styling to the content in the TinyMCE text editor. This is no longer the
// default text editor and "Atto" does not need this setting so we won't provide anything. If we did it would work the same
// as the previous setting - listing a file in the /styles/ folder.
$THEME->editor_sheets = [];

// This is a critical setting. We want to inherit from theme_classic because it provides a great starting point for SCSS bootstrap4
// themes. We have added add more than one parent here to inherit from multiple parents, and if we did they would be processed in
// order of importance (later themes overriding earlier ones). Things we will inherit from the parent theme include
// styles and mustache templates and some (not all) settings.
$THEME->parents = ['boost', 'classic'];

// A dock is a way to take blocks out of the page and put them in a persistent floating area on the side of the page.
// does not support a dock so we won't either - but look at bootstrapbase for an example of a theme with a dock.
$THEME->enable_dock = false;

// This is an old setting used to load specific CSS for some YUI JS. We don't need it in Classic based themes because Classic
// provides default styling for the YUI modules that we use. It is not recommended to use this setting anymore.
$THEME->yuicssmodules = array();

// Most themes will use this rendererfactory as this is the one that allows the theme to override any other renderer.
$THEME->rendererfactory = 'theme_overridden_renderer_factory';

$THEME->prescsscallback = 'theme_picture_get_pre_scss';

// Since we are using 2 parent themes the correct location of the layout files needs to be defined. For this theme we need the multiple
// column layouts.
$THEME->layouts = [
    // Most backwards compatible layout without the blocks - this is the layout used by default.
    'base' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array(),
    ),
    // Standard layout with blocks, this is recommended for most pages with general information.
    'standard' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
    ),
    // Main course page.
    'course' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
        'options' => array('langmenu' => true),
    ),
    'coursecategory' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // Part of course, typical for modules - default page layout if $cm specified in require_login().
    'incourse' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // The site home page.
    'frontpage' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
        'options' => array('nofullheader' => true),
    ),
    // Server administration scripts.
    'admin' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // My dashboard page.
    'mydashboard' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
        'options' => array('nonavbar' => true, 'langmenu' => true, 'nocontextheader' => true),
    ),
    // My public page.
    'mypublic' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    'login' => array(
        'theme' => 'boost',
        'file' => 'login.php',
        'regions' => array(),
        'options' => array('langmenu' => true),
    ),

    // Pages that appear in pop-up windows - no navigation, no blocks, no header.
    'popup' => array(
        'theme' => 'classic',
        'file' => 'contentonly.php',
        'regions' => array(),
        'options' => array('nofooter' => true, 'nonavbar' => true),
    ),
    // No blocks and minimal footer - used for legacy frame layouts only!
    'frametop' => array(
        'theme' => 'classic',
        'file' => 'contentonly.php',
        'regions' => array(),
        'options' => array('nofooter' => true, 'nocoursefooter' => true),
    ),
    // Embeded pages, like iframe/object embeded in moodleform - it needs as much space as possible.
    'embedded' => array(
        'theme' => 'boost',
        'file' => 'embedded.php',
        'regions' => array()
    ),
    // Used during upgrade and install, and for the 'This site is undergoing maintenance' message.
    // This must not have any blocks, links, or API calls that would lead to database or cache interaction.
    // Please be extremely careful if you are modifying this layout.
    'maintenance' => array(
        'theme' => 'boost',
        'file' => 'maintenance.php',
        'regions' => array(),
    ),
    // Should display the content and basic headers only.
    'print' => array(
        'theme' => 'classic',
        'file' => 'contentonly.php',
        'regions' => array(),
        'options' => array('nofooter' => true, 'nonavbar' => false),
    ),
    // The pagelayout used when a redirection is occuring.
    'redirect' => array(
        'theme' => 'boost',
        'file' => 'embedded.php',
        'regions' => array(),
    ),
    // The pagelayout used for reports.
    'report' => array(
        'theme' => 'classic',
        'file' => 'columns.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // The pagelayout used for safebrowser and securewindow.
    'secure' => array(
        'theme' => 'classic',
        'file' => 'secure.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre'
    )
];

Ready set go!

If you have been following along - now we are at the point where we can actually install and test our new theme. Try it now by visiting the admin notifications page to install the new plugin, and then choosing the new theme from the theme selector.

[Theme selector]

When you choose the new theme - you will find that it looks exactly the same as Classic. At this point with our minimal configuration - we are inheriting almost everything from our parent theme including styles and templates. You will notice though that we don't inherit the settings from our parent theme.

Setup your Sass files

Once you have a theme that has been installed into your Moodle setup it is time to start styling it. The core Boostrap scss is very well suited for customization using Sass variables and overriding component. On Get bootstrap.com there is some documentation on Sass variables and in theme/boost/scss/boostrap/_variables.scss you will see the full list of variables that can be changed.

Before we start the theme needs to be able to return the complete set of Sass files that are required for Moodle and our custom Sass. So as a start we will be editing the config again:

theme/picture/config.php

// This is the function that returns the SCSS source for the main file in our theme.
$THEME->scss = function($theme) {
    return theme_picture_get_main_scss_content($theme);
};

The variable $THEME->scss is configured to be a callback function that when called returns all the Scss source files from our parent theme and the picture theme. Once the callback function is defined our Theme's lib.php needs to contain the callback function.

theme/picture/lib.php

	/**
 * Returns the main SCSS content.
 *
 * @param theme_config $theme The theme config object.
 * @return string All fixed Sass for this theme.
 */
function theme_picture_get_main_scss_content($theme) {
    global $CFG;

    $scss = '';

    $fs = get_file_storage();

    // Main CSS - Get the CSS from theme Classic.
    $scss .= file_get_contents($CFG->dirroot . '/theme/classic/scss/classic/pre.scss');
    $scss .= file_get_contents($CFG->dirroot . '/theme/classic/scss/preset/default.scss');
    $scss .= file_get_contents($CFG->dirroot . '/theme/classic/scss/classic/post.scss');

    // Pre CSS - this is loaded AFTER any prescss from the setting but before the main scss.
    $pre = file_get_contents($CFG->dirroot . '/theme/picture/scss/pre.scss');

    // Post CSS - this is loaded AFTER the main scss but before the extra scss from the setting.
    $post = file_get_contents($CFG->dirroot . '/theme/picture/scss/post.scss');

    // Combine them together.
    return $pre . "\n" . $scss . "\n" . $post;
}

The function theme_picture_get_main_scss_content uses the scss files from classic, the default.scss file from classic contains @import statements to include the files from theme Boost so we can be sure we have all the css we need.

theme/classic/scss/preset/default.scss

// Import FontAwesome.
@import "fontawesome";

// Import All of Bootstrap
@import "bootstrap";

// Import Core moodle CSS
@import "moodle";

To complete the setup all we need to do is create the pre.scss and post.scss files that will contain our Sass variables and other Css. The pre.scss file is the file that needs to be loaded first before the files from theme classic are imported. It will contain the variable we configure for things like fonts, colours and spacing. The post.scss file will contain all other custom css.

theme/picture/scss/pre.scss

// We need larger buttons.
$btn-padding-y:         0.5rem !default;
$btn-padding-x:         1.1rem !default;

theme/picture/scss/post.scss

// We like our buttons very round.
.btn {
  border-radius: 1.078em;
  font-family: $font-family-sans-serif;
  font-size: 0.875em;
}

You might have noticed the "!default" statement after the variables in our pre.scss file. This means the variable is set if the variable has not been defined before. When inspecting the Bootstrap variables defined in theme/boost/scss/bootstrap/_variables.scss you will see all variable have a "!default" statement meaning we can change any variable!

With the preset files in place we can select our theme from the available installed themes (If you did not do that already) and purge changes. You should be able to see the Classic 3 column layout and our round buttons.

Adding custom settings to our theme

That's it for this tutorial, but there are more Themes docs to browse.