Chapter 19. Cascading Style Sheets

Table of Contents

1. Overview
2. Simple Views
3. Selectors
3.1. Attribute selectors
3.2. Element selectors
3.3. ID selectors
3.4. Descendant selectors
4. A Nested View
5. Dynamically-Created Views
6. Specificity
6.1. Lexical order
7. Importing an external stylesheet

Cascading Style Sheets (CSS) enable web designers to enhance the power of HTML tags. In old-fashioned HTML, an <H1> tag would make displayed text a little bigger and bolder, but font style, color, and size were left to the browser. With CSS, the web designer can use the <H1> tag to specify those font attributes.

CSS support within OpenLaszlo helps designers who are not fluent with the LZX scripting language maintain the appearance of Laszlo applications.

1. Overview

If an OpenLaszlo application is to be deployed more than once -- but with different colors, sizes, or resources in each deployment, for instance -- the designer can alter those attributes within a stylesheet.

2. Simple Views

This is the simplest example of an OpenLaszlo view:

Example 19.1. Simplest view

  <canvas>
      <view height="100" width="100" bgcolor="0x663399"/>
  </canvas>

To generate that view with a cascading stylesheet:

Example 19.2. Simplest view with CSS


<canvas>

<stylesheet>
    #gView {
        height: "100";
        width: "100";
        bgcolor: "0x663399";
    }
</stylesheet>

<view id="gView" height="$style{'height'}" width="$style{'width'}" bgcolor="$style{'bgcolor'}"/>

</canvas>


The $style constraint tells the OpenLaszlo runtime to style an attribute according to the stylesheet. More than one stylesheet can be used in an LZX document; the application of a stylesheet is determined by selectors.

3. Selectors

The stylesheet is a collection of rules that apply to the LZX document. Each rule applies to one aspect of the document, and consists of two parts: selector and declaration.

Example 19.3. Selector

#myView { bgcolor: "0x0000FF"; }

The selector comes before the curly brace, the declaration after. In the example above, an LZX element with id="myView" and bgcolor="$style{'bgcolor'} within its declaration will get a blue background color.

CSS support for OpenLaszlo enables four types of selectors:

  1. Attribute

  2. Element

  3. ID

  4. Descendant

3.1. Attribute selectors

Example 19.4. Attribute example

[attribute="value of attribute"] {
	property: "foo";
	}

The attribute selector applies when it corresponds to a node's attribute (for instance, name or width):

Example 19.5. Attribute selector


<canvas height="150" width="200">

<stylesheet>

[name="PurpleView"] {
	width: "100";
	height: "100";
	bgcolor: "0x9900FF"
}

[width="200"] {
	bgcolor: "0x0000FF";
}

</stylesheet>

<simplelayout axis="x" spacing="10"/>

<class name="myPurpleView">
	<view name="PurpleView" height="$style{'height'}" width="$style{'width'}" bgcolor="$style{'bgcolor'}"/>
</class>

<class name="myWiderView">
	<view name="hoo" height="100" width="200" bgcolor="$style{'bgcolor'}"/>
</class>

<myPurpleView/>
<myWiderView/>

</canvas>

The class myPurpleView contains a view whose name corresponds to the stylesheet selector [name="PurpleView"], so each of its $style rules is applied. The class myWiderView contains a view whose width fires the $style that applies to its background color.

3.2. Element selectors

Example 19.6. Element example

        LzNode {
            property: "foo";
        }

The element selector applies when it matches the name of the node itself:

Example 19.7. Element selector


<canvas height="200" width="200">
 
<stylesheet>
        view {
            nicebgcolor: "0x0000FF";
            micebgcolor: "0xCCCCCC";
        }
</stylesheet>

<view height="100" width="100" bgcolor="$style{'nicebgcolor'}"/>
<view height="75" width="75" bgcolor="0xFFFFFF"/>
<view height="50" width="50" bgcolor="$style{'micebgcolor'}"/>

</canvas>

3.3. ID selectors

Example 19.8. ID example

        #LzNode {
            property: "foo";
        }

ID selectors are identified by # in the stylesheet. The selector applies when it matches a node's ID:

Example 19.9. ID selector


<canvas height="200" width="200">

<stylesheet>

#red {
    width: 100;
    height: 100;
	bgcolor: "0xFF3333";
}

#blue {
    width: 150;
    height: 150;
	bgcolor: "0x3333FF";
}
 
</stylesheet>

<simplelayout axis="x" spacing="5" />

<class name="gBox" width="$style{'width'}" height="$style{'height'}" bgcolor="$style{'bgcolor'}" />

<gBox id="red" />
<gBox id="blue" />

</canvas>


3.4. Descendant selectors

Example 19.10. Descendant example

	fee fi {
        property: "car";
    }

	fee fi foo {
        property: "cart";
    }
    
    fee fi foo fum {
        property: "cartman";
    }

A selector in an ancestor/descendant hierarchy applies when its ancestors do. In the snippet above, the "fee" selector depends on no ancestors, so it would always fire on an LzNode identified by "fee". The "fum" selector, on the other hand, would only fire if the LzNode had three ancestors "fee", "fi", and "foo".

Example 19.11. Descendant selector


<canvas width="400" height="400">

<stylesheet>

    styledbox {
        stylebgcolor: "0xCCCCCC";
        styleinnercolor: "0xFF0000";
    }

    styledbox styledbox2 {
        stylebgcolor: "0x999999";
        styleinnercolor: "0x0000FF";
    }

    styledbox styledbox2 styledbox3 {
        stylebgcolor: "0x666666";
        styleinnercolor: "0x00FF00";
    }

</stylesheet>

<class name="styledbox" height="100" width="100" bgcolor="$style{'stylebgcolor'}">
	<view name="snow" x="10" y="10" height="10" width="10" bgcolor="$style{'styleinnercolor'}"/>
</class>

<class name="styledbox2" height="100" width="100" bgcolor="$style{'stylebgcolor'}">
	<view name="braxton" x="10" y="10" height="10" width="10" bgcolor="$style{'styleinnercolor'}"/>
</class>

<class name="styledbox3" height="100" width="100" bgcolor="$style{'stylebgcolor'}">
	<view name="jackson" x="10" y="10" height="10" width="10" bgcolor="$style{'styleinnercolor'}"/>
</class>

<styledbox id="comet" x="200" y="0">
			<styledbox2 id="shock" x="-100" y="100">
					<styledbox3 id="storm" x="-100" y="100"/>
			</styledbox2>
</styledbox>

<styledbox2/>
<styledbox3/>

</canvas>

In the example above, the instances of styledbox2 and styledbox3 at the end of the script don't appear on the canvas at all, because there's no stylesheet information for styledbox2 unless it descends from styledbox, and no stylesheet information for styledbox3 unless it descends from stylebox and stylebox2.

[Warning] Warning

Don't include an underscore in selector names, because the LZX file will not compile.

[Warning] Warning

The difference between an attribute selector and a descendant selector is as little as a spacebar. The attribute selector person[name='george'] (no space between person and name) selects a person whose name is george, but the descendant selector person [name='george'] selects a view named george which is a child of the view named person.

4. A Nested View

The advantage of using stylesheets shows in a slightly more difficult OpenLaszlo application. If a designer wanted to reuse a template, no knowledge of OpenLaszlo's LZX language is necessary -- in this example, s/he just has to change the color hexcode(s) and the name(s) of the resource(s) in the stylesheet:

Example 19.12. Nested view


<canvas height="350" width="700">

<stylesheet>

        #sun {
          bgcolor: "0x16355E"; 
          face: "../resources/smiley.gif";
        }
        
        #monarchs {
            bgcolor: "0xB2B9CB";
            face: "../resources/sourface.png";
        }
 
</stylesheet>

<simplelayout axis="x" spacing="10"/>

<class name="bouncebox">
        <view name="outer" x="0" y="0" width="200" height="200" bgcolor="$style{'bgcolor'}">
            <view name="inner" x="50" y="50" bgcolor="0xFFFFFF" width="${immediateparent.width-100}" height="${immediateparent.height-100}" />
        	<view name="gBounce" source="$style{'face'}" x="50" y="50">
				<animatorgroup name="myAnimatorGroup" start="true" process="sequential" >
				<animator attribute="y" from="50" to="150" duration="1000" motion="linear"/>
				<animator attribute="y" from="50" to="100" duration="1000" repeat="Infinity" motion="easeout"/>
   	   			</animatorgroup>
   	   		</view>
		</view>
</class>

<bouncebox id="monarchs" />
<bouncebox id="sun" />

</canvas>


5. Dynamically-Created Views

For styling a view created dynamically with new, it's easiest to define a class, then new instances of the class:

Example 19.13. Styled views created dynamically


<canvas debug="true">

<stylesheet>
mookie {
height : 25;
width : 25;
bgcolor : #0000FF;
}

blaylock {
title : "Blaylock";
bgcolor : #FF0000;
}

wilson {
height : 25;
width : 200;
fgcolor : #FF00FF;
text : "Wilson";
}
</stylesheet>

<class name="mookie" x="200" height="$style{'height'}" width="$style{'width'}" bgcolor="$style{'bgcolor'}"/>

<class name="blaylock">
<window title="$style{'title'}" x="250" height="100" width="150" bgcolor="$style{'bgcolor'}"/>
</class>

<class name="wilson" >
<text text="$style{'text'}" x="400" height="$style{'height'}" width="$style{'width'}" fgcolor="$style{'fgcolor'}"/>
</class>

<button text="Dynamically create Mookie">
<method event="onclick">
canvas.mookieView=new mookie(canvas, {});
</method>
</button>

<button text="Dynamically create Blaylock" y="50">
<method event="onclick">
canvas.blaylockWindow=new blaylock(canvas, {});
</method>
</button>

<button text="Dynamically create Wilson" y="100">
<method event="onclick">
canvas.wilsonText=new wilson(canvas, {});
</method>
</button>

</canvas>


6. Specificity

An instance of an LzNode -- most likely a view, or some other OpenLaszlo element -- can trigger more than one selector in the stylesheet. In such cases, CSS gives precedence according to rules of specificity. In general, the more specific the selector, the higher its precedence. For instance, the ID of a view is more specific (can only apply to one view) than view itself (applies to every view).

Within OpenLaszlo's CSS support, the order of specificity is (in ascending order):

  1. Element

  2. Attribute

  3. ID

This example uses all three selectors:

Example 19.14. Specificity


<canvas width="350" height="150">

<stylesheet>

view {
	height: "50";
	width: "50";
	bgcolor: "0xFF0000";
	}
	
[name='blue'] {
	height: "50";
	width: "50";
	bgcolor: "0x0000FF";
	}

#green {
	height: "50";
	width: "50";
	bgcolor: "0x006600";
	}

</stylesheet>

<simplelayout axis="x" spacing="25"/>

	<view id="red" name="red" width="$style{'width'}" height="$style{'height'}" bgcolor="$style{'bgcolor'}"/>
	<view id="blue" name="blue" width="$style{'width'}" height="$style{'height'}" bgcolor="$style{'bgcolor'}" />
	<view id="green" name="green" width="$style{'width'}" height="$style{'height'}" bgcolor="$style{'bgcolor'}" />

</canvas>


The first view -- red -- fires the element selector, because its ID is not "green", and its name is not "blue". It is an instance of a "view" element, which triggers the element selector. The second view -- blue -- fires the name selector; its ID is not "green", but the next selector in order of priority is attribute, which name='blue' matches. The third view -- green -- hits the highest-priority selector; id='green' matches the ID selector #green, and looks up that stylesheet.

[Warning] Warning

Style selectors: Under w3's CSS specificity rules, the highest-priority selector is "style='foo'", which directly looks up the foo stylesheet. The OpenLaszlo implementation does not support "style='foo'" because that expression cannot be used as a Laszlo property or attribute.

6.1. Lexical order

In some cases, more than one selector of the same type will be triggered. In this example, the view short fires two selectors: [height="50"] and [width="100"]. Neither selector takes precedence because they are both attribute selectors, so a tiebreaker has to be invoked. The selector closest to the bottom of the stylesheet prevails.

Example 19.15. Lexical order


<canvas height="200" width="345">

<stylesheet>
	
	[height="50"] {
	height: "50";
	width: "100";
	bgcolor: "0xFF0000";
	}
	
	[width="100"] {
	height: "50";
	width: "100";
	bgcolor: "0x0000FF";
	}
	
</stylesheet>

<simplelayout axis="y" spacing="10"/>

<view name="short" width="100" height="50" bgcolor="$style{'bgcolor'}"/>

<view y="75">
	<text text="If I'm red, the first stylesheet was triggered."/>
</view>

<view y="100">
	<text text="If I'm blue, the second stylesheet was triggered."/>
</view>

</canvas>

7. Importing an external stylesheet

A stylesheet can be imported by using

Example 19.16. Imported stylesheet clipping

   <stylesheet src="foo.css" />

when the stylesheet is in its typical format:

Example 19.17. Imported stylesheet example


<!--begin foo.css-->

#gPhilip {
    width: 300;
    height: 200;
    bgcolor: "0x8F008F";
}

<!--end-->

<!--The LZX file-->

<canvas>

	<stylesheet src="foo.css" />

	<view id="gPhilip" width="$style{'width'}" height="$style{'height'}" bgcolor="$style{'bgcolor'}"/>

</canvas>