Experimenting with Inheritance Basics
#1
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.


[Image: get-started-white.png]
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.

  1. Setting Up Child Themes

    To prepare two new Theme Packages, we'll go to inc/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-unused styles/dark.scss stylesheet.

      Specifically, we'll want to modify frontend/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 own frontend/assets.json file (equivalent to the same file in the Base Theme):
      {
      	"assets": {
      		"styles/dark.css": {
      			"source": "dark.scss",
      			"attached_to": [
      				{
      					"script": "global"
      				}
      			],
      			"depends_on": [
      				"styles/main.css"
      			]
      		}
      	}
      }
      
      This will result in dark.css being attached to all pages, after main.css.

      Finally, we also add a basic manifest.json file in the Package's main directory to declare inheritance from the Base Theme (codenamed core.base):
      {
      	"extra": {
      		"inherits": [
      			"core.base"
      		]
      	}
      }
      

      You may note that certain data in manifest files — like inheritance information — is contained in the extra 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 original styles/base/_colors.scss stylesheet within the frontend 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 the test-dark Theme:
      {
      	"extra": {
      		"inherits": [
      			"test-dark"
      		]
      	}
      }
      


    The resulting inc/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
    

    All inc/themes/ files created in this guide:
    .zip   test-themes.zip (Size: 3.34 KB / Downloads: 67)

  2. Using a Selected Theme Package

    Finally, we overwrite the currently-hardcoded Theme codename in global.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.


  3. 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.
    [Image: 19-test-dark-green-frame.png]

    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.


  4. 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.
    [Image: 19-test-dark-green-debug-frame.png]

    To take a better look at what's happening internally, head to the inc/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 the test-green Theme:

    • the final frontend/main.css and frontend/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 called resources.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 in assets.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 of dark.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; the shared 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 with base/_dark.scss and base/_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 final main.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 the cache/themelets/* directory for the respective Theme Package.

    We can also adjust the optimization level, currently set to Optimization::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.

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.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)