Table of Contents
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.
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.
This is the simplest example of an OpenLaszlo view:
Example 19.1. Simplest view
<canvas
height
="120
" width
="100%
">
<view
height
="100
" width
="100
" bgcolor
="#663399
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
To generate that view with a cascading stylesheet:
Example 19.2. Simplest view with CSS
<canvas
height
="120
" width
="100%
">
<stylesheet
>
#gView {
height: 100;
width: 100;
bgcolor: #663399;
}
</stylesheet
>
<view
id
="gView
" height
="$style{'height'}
" width
="$style{'width'}
" bgcolor
="$style{'bgcolor'}
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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.
The <boxmodel>
mixin
adds CSS2 model support to <view>
.
<boxmodel>
implements a subset of the CSS2 box model spec.
For a complete description of <boxmodel>
, see the
Reference.
Example 19.3. Simple view
with boxmodel
<canvas
height
="200
" width
="100%
" bgcolor
="antiquewhite
">
<stylesheet
>
#thebox {
padding: 1 3 5 7;
border-width: 2 4 6 8;
border-color: cornflowerblue;
margin: 3 7 11 15;
}
text { color: navy; }
.margin { background-color: aliceblue; }
.borderwidth { color: white; background-color: cornflowerblue; }
.padding { background-color: skyblue; }
.content { background-color: white; }
colortext { border-width: 1; }
colortext.margin, colortext.borderwidth { border-bottom-width: 0 }
</stylesheet
>
<class
name
="colorview
" extends
="view
">
<attribute
name
="bgcolor
" style
="background-color
" value
="transparent
"/>
<attribute
name
="fgcolor
" style
="color
" value
="black
"/>
</class
>
<class
name
="colortext
" extends
="text
" with
="boxmodel
" width
="100%
" multiline
="true
">
<attribute
name
="bgcolor
" style
="background-color
" value
="transparent
"/>
<attribute
name
="fgcolor
" style
="color
" value
="black
"/>
</class
>
<colorview
styleclass
="margin
" x
="5
" y
="5
">
<colorview
id
="thebox
" with
="boxmodel
" styleclass
="padding
" width
="250
">
<colorview
styleclass
="content
" layout
="axis: y; spacing:0
" width
="100%
">
<text
>Click me to demonstrate updating:
</text
>
<text
name
="op
"/>
<colortext
styleclass
="margin
">
<handler
name
="onmargin
" reference
="thebox
" method
="display
"/>
<handler
name
="oninit
" method
="display
"/>
<method
name
="display
" args
="ignore
">
this.format("margin: %s", thebox.presentAttribute('margin'));
</method
>
</colortext
>
<colortext
styleclass
="borderwidth
">
<handler
name
="onborderwidth
" reference
="thebox
" method
="display
"/>
<handler
name
="oninit
" method
="display
"/>
<method
name
="display
" args
="ignore
">
this.format("borderwidth: %s", thebox.presentAttribute('borderwidth'));
</method
>
</colortext
>
<colortext
styleclass
="padding
">
<handler
name
="onpadding
" reference
="thebox
" method
="display
"/>
<handler
name
="oninit
" method
="display
"/>
<method
name
="display
" args
="ignore
">
this.format("padding: %s", thebox.presentAttribute('padding'));
</method
>
</colortext
>
<attribute
name
="attribute
" type
="string
"/>
<attribute
name
="amount
" type
="number
"/>
<handler
name
="onclick
" method
="doit
"/>
<handler
name
="oninit
" method
="doit
"/>
<method
name
="doit
" args
="ignore
">
if (this.attribute) {
thebox.setAttribute(this.attribute, thebox[this.attribute] + amount);
}
var r = Math.floor(Math.random()*3);
var prefix = ['margin', 'border', 'padding'][r];
var suffix = ['','width',''][r];
this.attribute = prefix+['top','right','bottom','left'][Math.floor(Math.random()*4)]+suffix;
this.amount = Math.round(Math.random()*10);
op.format("%s += %w", attribute, amount);
</method
>
</colorview
>
</colorview
>
</colorview
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2011 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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.
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:
Attribute
Element
ID
Descendant
The attribute selector applies when it corresponds to a node's attribute (for
instance, name
or width
):
Example 19.6. Attribute selector
<canvas
height
="120
" width
="100%
">
<stylesheet
>
[name="PurpleView"] {
width: 100;
height: 100;
bgcolor: #9900FF
}
[width="200"] {
bgcolor: #0000FF;
}
</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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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.
The element selector applies when it matches the tag that creates the node. In the
example below, the element selector view
matches the
<view>
tag:
Example 19.8. Element selector
<canvas
height
="120
" width
="100%
">
<stylesheet
>
view {
nicebgcolor: #0000FF;
micebgcolor: #CCCCCC;
}
</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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
ID selectors are identified by # in the stylesheet. The selector applies when it matches a node's ID:
Example 19.10. ID selector
<canvas
height
="170
" width
="100%
">
<stylesheet
>
#red {
width: 100;
height: 100;
bgcolor: #FF3333;
}
#blue {
width: 150;
height: 150;
bgcolor: #3333FF;
}
</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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Example 19.11. 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 node identified by "fee". The "fum" selector, on the other hand, would only fire if the node had three ancestors "fee", "fi", and "foo".
Example 19.12. Descendant selector
<canvas
height
="320
" width
="100%
">
<stylesheet
>
styledbox {
stylebgcolor: #CCCCCC;
styleinnercolor: "red";
}
styledbox styledbox2 {
stylebgcolor: #999999;
styleinnercolor: "blue";
}
styledbox styledbox2 styledbox3 {
stylebgcolor: #666666;
styleinnercolor: "green";
}
</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
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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 | |
---|---|
Don't include an underscore in selector names, because the LZX file will not compile. |
Warning | |
---|---|
The difference between an attribute selector and a descendant selector is as
little as a spacebar. The attribute selector |
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.13. Nested view
<canvas
height
="220
" width
="100%
">
<stylesheet
>
#sun {
bgcolor: #16355E;
face: "resources/smiley.gif";
}
#monarchs {
bgcolor: #B2B9CB;
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
For styling a view created dynamically with new
, it's easiest to
define a class, then new
instances of the class:
Example 19.14. Styled views created dynamically
<canvas
height
="150
" width
="100%
">
<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
">
<handler
name
="onclick
">
canvas.mookieView=new lz.mookie(canvas, {});
</handler
>
</button
>
<button
text
="Dynamically create Blaylock
" y
="50
">
<handler
name
="onclick
">
canvas.blaylockWindow=new lz.blaylock(canvas, {});
</handler
>
</button
>
<button
text
="Dynamically create Wilson
" y
="100
">
<handler
name
="onclick
">
canvas.wilsonText=new lz.wilson(canvas, {});
</handler
>
</button
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
CSS attribute selectors automatically update when the value of an attribute specified by
the selector changes. For example: button[mouse=down]
will listen for
changes to the mouse
attribute and will only apply when
button.mouse == 'down'
.
The following example demonstrates the use of dynamic CSS. Click on the color buttons to toggle the colors; click on the color swatches to see the CSS description.
Example 19.15. Dynamic CSS
<canvas
width
="100%
" height
="500
" debug
="true
">
<debug
x
="30%
" width
="65%
" y
="5%
" height
="90%
"/>
<stylesheet
>
/* default, should only be seen if things are broken */
colorswatch { background-color: orange }
/* Static for buttons */
colorbutton[color=red] {background-color: red }
/* `lime` is the HTML name for rgb(0,255,0) */
colorbutton[color=green] {background-color: lime }
colorbutton[color=blue] {background-color: blue }
/* dynamic single selectors that apply to individual swatches */
[red=off] colorswatch { background-color: cyan; opacity: 0.2 }
[red=on] colorswatch { background-color: red; opacity: 0.95 }
[green=off] colorswatch { background-color: magenta; opacity: 0.2 }
[green=on] colorswatch { background-color: lime; opacity: 0.95 }
[blue=off] colorswatch { background-color: yellow; opacity: 0.2 }
[blue=on] colorswatch { background-color: blue; opacity: 0.95 }
/* dynamic compound selectors that apply to mixer colorswatch */
[red=off] [green=off] [blue=off] [name=mixer] colorswatch { background-color: black; opacity: 1 }
[red=off] [green=off] [blue=on] [name=mixer] colorswatch { background-color: blue; opacity: 1 }
[red=off] [green=on] [blue=off] [name=mixer] colorswatch { background-color: lime; opacity: 1 }
[red=off] [green=on] [blue=on] [name=mixer] colorswatch { background-color: cyan; opacity: 1 }
[red=on] [green=off] [blue=off] [name=mixer] colorswatch { background-color: red; opacity: 1 }
[red=on] [green=off] [blue=on] [name=mixer] colorswatch { background-color: magenta; opacity: 1 }
[red=on] [green=on] [blue=off] [name=mixer] colorswatch { background-color: yellow; opacity: 1 }
[red=on] [green=on] [blue=on] [name=mixer] colorswatch { background-color: white; opacity: 1 }
</stylesheet
>
<!-- button to toggle an attribute which should trigger style change -->
<class
name
="colorbutton
" width
="40
" height
="20
" align
="center
">
<attribute
name
="bgcolor
" style
="background-color
"/>
<attribute
name
="color
" type
="string
"/>
<text
fgcolor
="white
" text
="${this.parent.on}
" align
="center
"/>
<attribute
name
="on
" type
="string
" value
="off
"/>
<handler
name
="onclick
">
this.setAttribute('on', this.on === 'on' ? 'off' : 'on' );
</handler
>
</class
>
<!-- color swatch that is styled by background-color and opacity -->
<class
name
="colorswatch
" height
="60
" width
="60
" bgcolor
="gray
" align
="center
">
<attribute
name
="swatchcolor
" style
="background-color
" type
="color
"/>
<attribute
name
="swatchopacity
" style
="opacity
" value
="1
"/>
<handler
name
="onclick
">
Debug.clear();
Debug.explainStyleBindings(this);
</handler
>
<view
name
="swatch
" x
="5
" y
="5
" height
="50
" width
="50
">
<attribute
name
="bgcolor
" value
="${parent.swatchcolor}
"/>
<attribute
name
="opacity
" value
="${parent.swatchopacity}
"/>
</view
>
</class
>
<!-- nested views with red, green, blue attributes that dynamically change
to demonstrate the applicability of compound dynamic selectors -->
<view
x
="5%
" y
="5%
" width
="20%
" height
="80%
" layout
="axis: y; spacing: 5
">
<text
width
="100%
" multiline
="true
">
Toggle the red, green, and blue buttons on and off to
dynamically update the applicable CSS selectors. Click on a color
swatch to explain the applicability of the CSS selectors.
</text
>
<attribute
name
="red
" value
="${this.toggle.on}
" type
="string
"/>
<colorbutton
name
="toggle
" color
="red
"/>
<colorswatch
/>
<view
width
="100%
" layout
="axis: y; spacing: 5
">
<attribute
name
="green
" value
="${this.toggle.on}
" type
="string
"/>
<colorbutton
name
="toggle
" color
="green
"/>
<colorswatch
/>
<view
width
="100%
" layout
="axis: y; spacing: 5
">
<attribute
name
="blue
" value
="${this.toggle.on}
" type
="string
"/>
<colorbutton
name
="toggle
" color
="blue
"/>
<colorswatch
/>
<view
width
="100%
" name
="mixer
" layout
="axis: y; spacing: 5
">
<text
align
="center
">Mixer
</text
>
<colorswatch
id
="mixer
"/>
</view
>
</view
>
</view
>
</view
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2010 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
An instance of a node -- 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):
Element
Attribute
ID
This example uses all three selectors:
Example 19.16. Specificity
<canvas
height
="70
" width
="100%
">
<stylesheet
>
view {
height: 50;
width: 50;
bgcolor: #FF0000;
}
[name='blue'] {
height: 40;
width: 40;
bgcolor: "blue";
}
#green {
height: 30;
width: 30;
bgcolor: rgb(0,102,0);
}
</stylesheet
>
<view
layout
="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
" width
="$style{'width'}
" height
="$style{'height'}
" bgcolor
="$style{'bgcolor'}
"/>
</view
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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 | |
---|---|
Style selectors: Under W3C'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. |
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.17. Lexical order
<canvas
height
="120
" width
="100%
">
<stylesheet
>
[height="50"] {
height: 50;
width: 100;
bgcolor: #FF0000;
}
[width="100"] {
height: 50;
width: 100;
bgcolor: #0000FF;
}
</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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
A stylesheet can be imported by using
when the stylesheet is in its typical format:
Example 19.19. Imported stylesheet example
<!--begin foo.css--> #gPhilip { width: 300; height: 200; bgcolor: #8F008F; } <!--end--> <!--The LZX file--> <canvas> <stylesheet src="foo.css" /> <view id="gPhilip" width="$style{'width'}" height="$style{'height'}" bgcolor="$style{'bgcolor'}"/> </canvas>
Copyright © 2002-2010 Laszlo Systems, Inc. All Rights Reserved. Unauthorized use, duplication or distribution is strictly prohibited. This is the proprietary information of Laszlo Systems, Inc. Use is subject to license terms.