BIS Lightweight Application Development Engine

Creating and Editing Themes


Introduction

BLADE themes are simply a collection of css classes and javascript functions which modify css attributes, all of which give components a particular look and feel. In early versions of BLADE, changing a theme meant going in and editing the CSS and JS files directly. While its still possible to do this, the default BLADE themes use an XML-based format that allows inheritance between themes. There are two benefits to this approach. First, it simplifies extending existing themes. Second, by basing all rollover themes off a "root" theme, it simplifies extending the rollover widget while maintaining compatibility with existing themes.

As alluded to in the Architecture page, BLADE themes are broken up into elements and states. Consider the rollover, one of the simplest components. It has 4 elements: a root element (the table), and left, center, and right, the three table cells. It also has 3 states (actually closer to state transitions): render, the state for when it is initially loaded onto the page, rollon (during a mouseover), and rolloff (when someone mouses out).

Since the render state happens when the page is loaded, it can be defined using CSS attributes. Since the rollon and rolloff states are controlled by javascript, their attributes have to be defined using js's hacked up interpretation of css classes. Javascript doesn't like hyphens in variable names. As a result, all css attributes with hyhpens have to be converted to non-hyphen equivalents. Unfortunately, there isn't a clean, uniform way to do this. For example, background in javascript can be used to control either background-image or background-color.

As an example, lets look at the nblue theme:

<widget name="rollover" theme="nblue" extends="root">
	<state name="render">
		<element name="main">
			<height value="30px"/>
		</element>
		<element name="left">
			<width value="15px"/>
			<background-image value="url("$cr/images/buttons/nblue-left.gif")"/>
		</element>
		<element name="center">
			<color value="white"/>
			<background-image value="url("$cr/images/buttons/nblue-center.gif")"/>
		</element>
		<element name="right">
			<width value="15px"/>
			<background-image value="url("$cr/images/buttons/nblue-right.gif")"/>
		</element>
	</state>
</widget>
The opening line specifies what type of widget we're defining a theme for, the theme name, and what theme we're extending. The root theme we extend defines a few attributes common to all rollovers: the cursor looks like a pointer, the caption is centered, etc. The next line opens the definition for the render state. The lines after that define each element and the attributes for those elements.



Defining Widgets and Attributes

The theme compiler also needs a list of all the states and elements that make up a widget. This is provided through the just-under-top-level widget-definition tags. The definition for the rollover widget looks like this:

<widget-definition name="rollover">
	<elements>
		<element tagtype="table" name="main"/>
		<element tagtype="tr" name="main"/>
		<element tagtype="td" name="left"/>
		<element tagtype="td" name="center"/>
		<element tagtype="td" name="right"/>
	</elements>
	<states>
		<state name="render"/>
		<state name="rollon"/>
		<state name="rolloff"/>
	</states>
</widget-definition>
The elements tag holds a list of all possible elements, and the states tag holds a list of all hte possible states. Note that the element tags also have a tagtype attribute, which tells the compiler what type of HTML tag the element corresponds to. In the event where the same element name corresponds to multiple tagtypes (as seen here with the button element), you can place a tagtype attribute on the element tag in the theme definition to indicate which element you're referring to. Since this is not done in the nblue theme shown above, the height: 30 css attribute gets applied to both the table and tr tags.



Imports

The widget definitions and root themes probably aren't going to exist in the xml file your theme is in. In order to support this, you can place import and include commands at the top of the theme's xml file, like this:

<import href="location of file"/>
<include pkg="package name"/>
Imported files act as if they are part of the current file. If a theme is defined in the imported file, it will be compiled alongside all the themes in the importing file. Imports are generally used to reference the widget definitions. Themes in included packages are not compiled, but can be extended.

If theme A extends theme B, but A and B are in different directories, some serious problems arise. For example, B might have a background-image of background.gif. When A extends B, it will copy the background-image attribute as background.gif, even though there is no such file in A's folder. Blade solves this by using several variables in the value attribute: $cr/, $pr/, and $br/. $cr/ makes a reference to the CSS Root of the theme, and should be used before CSS background-images. $pr/ makes a reference to the package root of the theme, and should be used before image URLs and JS background images. $br/ references the BLADE root directory, and should be used for images that are in a fixed subdirectory from the BLADE root.



Configuration

To get a theme to compile properly, two more things need to be done. First, a context tag needs to be added to the theme definition to tell the compiler which theme the definition represents. It looks like this:

<context>
	<package-name value="package name"/>
</context>
Additionally, in the imports section of app/config.xml, you need to to add an import-theme statement that points to the new themepack.



Compiling Themes

Actually compiling themes is the easy part. Go to the tools directory and run build.bat. It automatically reads config.xml, determines dependencies between themes based on the include statements, and then compiles the themes listed in config.xml. One important thing to keep in mind is that, in order for the compiler to work automatically, themes should avoid circular includes: if A includes B, B should not include A.