Bespin Plugin Guide

Themes

Bespin allows users to change themes (color schemes) and to create new theme plugins as well. This document describes how you can create your own themes and gives a basic overview about the theming techniques used in Bespin.

The problem

Much of Bespin's UI (login form, command line) is themed using CSS. Imagine someone wants the whole UI background, including that of the command line, to be green. If you use CSS, this means you have to replace the entire CSS file only to change one variable - in this case the background color to green. If you could use variables in CSS, you could add a background variable and define a color in the theme data.

LESS extends CSS

CSS has no way of dealing with variables, which is why Bespin uses LESS instead:

LESS extends CSS with: variables, mixins, operations and nested rules. (Quoted from the LESS homepage.)

We use themeVariables to customize the CSS that LESS ultimately generates. You can't customize the UI 100%, because then every little bit has to be a themeVariable, but you can change most of the colors and a few more things. If that's not enough for your use case, you still can override the LESS file provided by a plugin with your own styles.

When you define a new theme, you have to create only a set of colors, fonts etc. that is then available to each plugin. We call these the global themeVariables. Plugins that provide user interface elements should use these variables to adjust their design. This ensures that whichever plugin a user installs looks right as they apply a theme.

How this works

The theme_manager plugin is responsible to, surprise!, managing the themes. It loads all of the LESS files defined within the plugins and passes the plugin-specific themeVariables as well as the global themeVariables to it. The generated CSS code is then added to the page.

Writing your own theme

If you want to create your own theme, create a new plugin - let's say 'demo_theme'. Themes have to be defined in the metadata of the plugin. The package.json file inside of the 'demo_theme' folder might look like this:

{
    "description": "Provides a basic demo theme",
    "provides": [
        {
            "ep": "theme",
            "name": "demoTheme",
            "description": "Provides a basic demo theme",
            "pointer": "index#demoTheme"
       }
    ]
}

The name provided in the metadata is what the user will type in when they're changing the theme. In this case, the user will type set theme demoTheme.

Create a new file index.js inside of the 'demo_theme' folder. This is the place where the themeVariable definitions will get stored. When the theme_manager plugin loads the demoTheme, it calls the function defined in the pointer property. This function has to return the themeVariables:

// This goes in the index.js file.

/**
 * Exports the demoTheme function that is called by the theme_manager plugin.
 */
exports.demoTheme = function() {
    return {
        // Theme variables that are available to every plugin.
        global: {
            // Defines the color for links.
            link_color: 'blue',

            [...]
        },

        // Theme variables for the editor.
        text_editor: {
            // Theme of the gutter:
            gutter: {
                [...]
            },

            editor: {
                [...]
            },

            highlighter: {
                [...]
            },

            scroller: {
                [...]
            }
        },

        // Defines themeVariables for the command_line plugin.
        command_line: {
            // Defines the color for links.
            link_text: 'red';
        }
    };
};

This is the basic structure of a theme definition. For a complete overview of all the adjustable themeVariables in the global and text_editor section, take a look at the whiteTheme.

In the example, we define the themeVariables for the global themeVariables (stored in global), the text_editor plugin and the command_line plugin. Within the command_line's metadata the link_text is defined as:

    {
        "ep": "themevariable",
        "name": "link_text",
        "defaultValue": "@global_link_color"
    }

Every defined themeVariable has a defaultValue. This value is used if no other value is specified. In the case of the demoTheme, the value for the command_line's link_text will be red. If we hadn't declared this, the defaultValue will be used. As this value is equal to the global link_color, the value is then blue.

Note that it's generally a good idea to focus on the global variables as much as possible. There's no predicting which plugins a user is likely to have installed!

Custom CSS

If adjusting the themeVariables is not enough for you, you can provide your own LESS file. The CSS rules from the LESS file will then override the CSS rules that are included inside of the plugins. The theme_manager has to know which LESS file you want to load with the theme, so you have to declare it inside of the themes metadata:

{
    "description": "Provides a basic demo theme",
    "provides": [
        {
            "ep": "theme",
            "name": "demoTheme",
            "description": "Provides a basic demo theme",
            "url": [
                "theme.less"
            ],
            "pointer": "index#demoTheme"
       }
    ]
}

As the demoTheme gets activated, the theme_manager will handle the defined themeVariables and load up the files defined in the url property - in this case the theme.less file. This file has to be located in the resources folder which has to be inside of the plugin folder. You can use the whiteTheme as an example.