2024-06-28, 05:11 PM
(This post was last modified: 2024-06-28, 05:12 PM by Devilshakerz. Edited 1 time in total.)
MyBB 1.9, in addition to a new default look, brings a major expansion of the underlying engine for a greater ease of styling and customization. In this thread, we'll demystify the overhauled system and walk through a few basics of Theme inheritance using a real-world example.
To follow along, prepare a MyBB 1.9 installation using the Quick Start guide.
To test the new system, we'll create two new, separate Themes to apply a few changes to the Base Theme's stylesheets. We'll use one child Theme to force dark mode, another Theme to change the primary color, and combine the effects with inheritance declarations.
Open by Design
While a number of tasks will be covered by the Admin CP interface layer, you may note that the new system requires no boilerplate to start working on new Extensions; is open to modification using files alone, in a commonly recognized format; and offers greater visibility into its state, including cache content, processing steps, and runtime performance. This is part of our focus on developer experience.
To follow along, prepare a MyBB 1.9 installation using the Quick Start guide.
Experimenting with Inheritance Basics
To test the new system, we'll create two new, separate Themes to apply a few changes to the Base Theme's stylesheets. We'll use one child Theme to force dark mode, another Theme to change the primary color, and combine the effects with inheritance declarations.
- Setting Up Child Themes
To prepare two new Theme Packages, we'll go toinc/themes/
and create two more directories there, named according to their codename:
test-dark
In this Package, we'll overwrite one file with minor adjustments, and attach the currently-unusedstyles/dark.scss
stylesheet.
Specifically, we'll want to modifyfrontend/styles/base/_dark.scss
. To overwrite the file using inheritance, we simply add it at the same path as in the Base Theme in the new Package's directory. Then, we can make our edits — adding a!default
Sass flag to the variable to prevent the primary color from being overwritten by the dark mode scheme:
$primary-color: #005084 !default;
With our second objective being to attach the file that activates the dark mode ($dark: true;
), we declare it in the Theme's ownfrontend/assets.json
file (equivalent to the same file in the Base Theme):
This will result in{ "assets": { "styles/dark.css": { "source": "dark.scss", "attached_to": [ { "script": "global" } ], "depends_on": [ "styles/main.css" ] } } }
dark.css
being attached to all pages, aftermain.css
.
Finally, we also add a basicmanifest.json
file in the Package's main directory to declare inheritance from the Base Theme (codenamedcore.base
):
{ "extra": { "inherits": [ "core.base" ] } }
You may note that certain data in manifest files — like inheritance information — is contained in theextra
field. This makes it possible to keep them forward-compatible with Composer's schema, and eventually installable using the same way as MyBB's core dependencies.
test-green
In this Package, we'll change the primary color of the Theme. As 1.9's Base Theme uses variables for colors and other values, we'll only need to modify a single declaration.
We copy the originalstyles/base/_colors.scss
stylesheet within thefrontend
namespace, and edit chosen variables:
$primary-color: green;
Similarly, we declare the desired inheritance in the second Theme's manifest file — this time making it inherit from thetest-dark
Theme:
{ "extra": { "inherits": [ "test-dark" ] } }
The resultinginc/themes/
directory, containing all source Packages, should now look like this:
inc/ └── themes/ ├── core.base/ │ └── ... ├── test-dark/ │ ├── frontend/ │ │ ├── styles/ │ │ │ └── base/ │ │ │ └── _dark.scss │ │ └── assets.json │ └── manifest.json └── test-green/ ├── frontend/ │ └── styles/ │ └── base/ │ └── _colors.scss └── manifest.json
- Using a Selected Theme Package
Finally, we overwrite the currently-hardcoded Theme codename inglobal.php
:
$packageName = 'test-green';
This reference will eventually be managed in a similar way as in the past: taking into account settings and preferences for the default, user-, and forum-specific Themes.
- Observing Results
On the first run, various data layers will be warmed up, with the new CSS files being generated and published at the end, ready to be included in the HTTP response.
In the page source, we can see that our definitions result in the correct order, and local files are accompanied by a timestamp that helps avoid stale cache issues:
<link rel="stylesheet" href="https://localhost/cache/themelets/test-green/frontend/main.css?t=1718649384"> <link rel="stylesheet" href="https://localhost/cache/themelets/test-green/frontend/dark.css?t=1718649385">
Notably, compared to MyBB ≤ 1.8, all Assets are published in relation to the requested Theme, and don't reveal the internal hierarchy that's been set up.
- Peeking Under the Hood
If you happen to use the debug mode (?debug=1
) when the Theme is generated, you'll be able to see measurements related to generating i.a. the hierarchical resolution cache, and Asset compilation.
To take a better look at what's happening internally, head to theinc/cache/themelets/
directory, which contains published Assets and cache stored in a human-readable JSON format.
Currently, the following structure will be created with data for thetest-green
Theme:
- the final
frontend/main.css
andfrontend/dark.css
files generated by the SCSS compiler,
accompanied by internal files:
hierarchy.ancestors.json
, storing the determined hierarchy — from closest to furthest ancestor.
As expected, the final Theme inherits from two other themes, in the following order:
"value": [ "test-dark", "core.base" ]
As changes to manifest files may result in differing hierarchy, the files are "stamped" to re-generate the structure when modifications are made.
hierarchy.resolution.resources.json
, the inheritance resolution cache — a structure mapping Resources to correct Themelets (which may also belong to Plugins), in each namespace.
In our case, this is a result of the ancestry declared in manifest files, and each Resource file's existence in queried Themes, where descendant Themes have higher priority.
Conveniently, the modified files top the alphabetically-sorted list, and appear to resolve to the respective Theme Packages in which they were modified:
{ "frontend": { "stamp": { "test-green": null, "test-dark": null, "core.base": null }, "value": { "styles\/base\/_colors.scss": "test-green", "styles\/base\/_dark.scss": "test-dark", "styles\/base\/_module.scss": "core.base", "styles\/base\/_settings.scss": "core.base", "styles\/base\/_typography.scss": "core.base",
In this data layer, the "stamped" sources are files calledresources.json
— containing Resource definitions — given that inheritance may also be modified there (but in this example, the installation doesn't include any).
hierarchy.properties.assets.json
, the combined properties of Assets, resolved according to declarations inassets.json
files (similarly "stamped" if detected), per namespace.
Here, we can see that the structure correctly merges properties from the Base Theme with the subsequently added declaration ofdark.css
:
{ "frontend": { "stamp": { "test-green": null, "test-dark": { "checksum": "7342713e1249ae1e61f4c3537ca2d41c", "time": 1718488800 }, "core.base": { "checksum": "ec24abd3b1f9ea098e439d12a93df6b7", "time": 1716577733 } }, "value": { "shared": [], "entity": { "styles\/dark.css": { "source": "dark.scss", "attached_to": [ { "script": "global" } ], "depends_on": [ "styles\/main.css" ] }, "styles\/main.css": { "source": "main.scss", "attached_to": [ { "script": "global" } ] },
All included definitions are considered entity-bound; theshared
structure may be conversely populated by higher-level declarations, which may be particularly useful for managing inheritance for a complete namespace.
resolvedResources
, a "flattened" directory with a resolved set of Resources. At present, this includes styles collected from the whole inheritance hierarchy.
Following our modifications, it contains default files from the Base Theme withbase/_dark.scss
andbase/_colors.scss
overwritten down the chain, effectively resembling a single, final Theme.
The directory is generated on demand for the SCSS compiler, and modifying it later will have no effect; however, the contents are left for easier troubleshooting and consumption by third-party processing tools.
publication.json
, containing Asset source files. The structure lists all the ingredients used to generate and publish each Asset.
In addition to paths, the cache identifies Themelets from which particular Resources originate. This helps in keeping track of Resource-Asset relationships, and propagating changes made to individual files.
In this file, we can see that, for example, the two modified source files have successfully made it to the finalmain.css
file from the correct Packages:
"@frontend\/styles\/main.css": { "sources": [ { "themelet": "core.base", "subPath": "main.scss" }, { "themelet": "core.base", "subPath": "base\/_module.scss" }, { "themelet": "test-green", "subPath": "base\/_colors.scss" }, { "themelet": "core.base", "subPath": "base\/_settings.scss" }, { "themelet": "core.base", "subPath": "base\/_typography.scss" }, { "themelet": "test-dark", "subPath": "base\/_dark.scss" },
The cache layers can be re-generated by deleting any chosen file(s) in thecache/themelets/*
directory for the respective Theme Package.
We can also adjust the optimization level, currently set toOptimization::WATCH
, to options available here.
To better understand the interaction between Resources and Assets, see the Asset Management section and the Diagram: Asset Publishing and Management.
Even though the system will be expanded, this will help you hit the ground running when creating and customizing 1.9 Themes.
- the final
Open by Design
While a number of tasks will be covered by the Admin CP interface layer, you may note that the new system requires no boilerplate to start working on new Extensions; is open to modification using files alone, in a commonly recognized format; and offers greater visibility into its state, including cache content, processing steps, and runtime performance. This is part of our focus on developer experience.