Frontal Style Sheets
From Frontal Wiki
Frontal uses cascading style sheets to allow the user to selectively apply styles and attributes to elements in the document. Styles are primarily used to effect how the renderer will display Frontal elements, and attributes are primarily used to apply interactions to those elements. For example, here we use styles to set the width, height and background color of a 'div' element. We use an attribute to add an 'onClick' interaction that changes the background color randomly.
<div style="width: 25%; height: 25%; float: left; background-color: green;" onClick="sS ( 'background-color', Math.floor ( 0xffffff * Math.random ( ) ) );" />
This is fine but what if we want 4 divs? We could copy this four times:
<div style="width: 25%; height: 25%; float: left; background-color: green;" onClick="sS ( 'background-color', Math.floor ( 0xffffff * Math.random ( ) ) );" /> <div style="width: 25%; height: 25%; float: left; background-color: green;" onClick="sS ( 'background-color', Math.floor ( 0xffffff * Math.random ( ) ) );" /> <div style="width: 25%; height: 25%; float: left; background-color: green;" onClick="sS ( 'background-color', Math.floor ( 0xffffff * Math.random ( ) ) );" /> <div style="width: 25%; height: 25%; float: left; background-color: green;" onClick="sS ( 'background-color', Math.floor ( 0xffffff * Math.random ( ) ) );" />
But this becomes messy pretty quickly. And it's a pain to maintain. What if we want to add a border to all of our divs? Or change their shape? Wouldn't it be convenient if we could make those changes in one place and have it effect all of our divs? Yes it would, and this is where style sheets come into play.
Contents |
Defining a Style Sheet
A style sheet is created with the 'style' tag. Inside of the style sheet are any number of rule sets. A general rule set looks like the following, though in practice they're usually not so complex:
selector1, selector2, ... selectorN { styleDeclaration1: styleValue1; styleDeclaration2: styleValue2; ... styleDeclarationN: styleValueN; @attributeDeclaration1 { attributeValue1 } @attributeDeclaration2 { attributeValue2 } ... @attributeDeclarationN { attributeValueN } // The following will be discussed in the Basic Topics section of this guide. // +onMatch { onMatchValue } +onMismatch { onMismatchValue } +rulesetId: rulesetId }
The selectors on a rule set are a sort of pattern that must be matched before the rule set is applied to an element in our Frontal document. The simplest selector is a tag name. For example, the selector 'div' would match every div element in our document. That sounds useful for our example so let's set up our style sheet with it:
<style><![CDATA[ div { } ]]></style>
Looking back at the general rule set above, we see there are some number of selectors and then some curly braces that surround what we call the rule set's declarations. These declarations are a list of styles and attributes to apply to any element that matches the rule set's selectors. If we look at our example, we see a number of common styles and interactions on our 'div' tags, so lets put those in our style sheet:
<style><![CDATA[ div { width: 25%; height: 25%; float: left; background-color: green; @onClick { sS ( 'background-color', Math.floor ( 0xffffff * Math.random ( ) ) ); } } ]]></style>
The first thing to note here is that attribute declarations always start with an "@" sign. Up to this point, those familiar with CSS3 (a standard used in web browsers upon which Frontal's style sheets are based) have probably been feeling pretty sure of themselves, but this is something new. With Frontal's style sheets, you can effect not only styles, but attributes as well. Not only is this very handy for applying an interaction (as we do in this example), it's useful for applying any arbitrary attribute. Towards that end, there are two syntaxes for assigning a value to an attribute: either enclose the value in curly braces, or separate it from the attribute's name with a colon. The former syntax supports multi-line values, whereas the latter doesn't. So in the above example, we also could've written our 'onClick' attribute declaration as follows:
@onClick { sS ( 'background-color', Math.floor ( 0xffffff * Math.random ( ) ) ); }
So before we look at the other elements of a rule set, let's rewrite our original example using the stylesheet we've created:
<style><![CDATA[ div { width: 25%; height: 25%; float: left; background-color: green; @onClick { sS ( 'background-color', Math.floor ( 0xffffff * Math.random ( ) ) ); } } ]]></style> <div /><div /><div /><div />
That's much cleaner and makes it much easier for us to make changes. For example, let's change the shape of our div elements and add a new div on each click:
<style><![CDATA[ div { background-shape: ellipse; width: 25%; height: 25%; float: left; background-color: green; @onClick { sS ( 'background-color', Math.floor ( 0xffffff * Math.random ( ) ) ); document.write ( "<div />"); } } ]]></style> <div /><div /><div /><div />
Before employing a style sheet, we would've had to make these changes in four places. And we would've had to apply them every time we added a new div! That wouldn't have been fun.
In the example above, we used a very simple selector ('div') to match elements. In the next section, we'll look at how flexible selectors may be.
Selectors
We use selectors to indicate which document elements a rule set should be applied to. As you can imagine, there are lots of different cases to try to handle, and so there are lots of different kinds of selectors.
Element Selectors
Here are the simpler selectors:
- '*' is the universal selector. It matches any element.
- "E" stands for a type selector (or element). Because there are a multitude of type selectors out there (like 'div', 'img', 'text', etc), for simplicity's sake, we'll use "E" to represent them. So, we can replace "E" with any tag name to then match all such elements. In the following example, "E" is replaced by 'img':
* { border-width: 1px; } img { alpha: .5; }
You may group selectors by separating them with a comma. They act independently of one another to match elements. For example, this will set the 'blur' of 'div' and 'text' elements:
div, text { blur: 5; }
Note that in our further examples of selectors, we'll continue to use the "E" symbol to stand in for an element name. The "E" may be omitted though and in that case, the universal selector is assumed. For example, these two selectors are equivalent:
*.green .green
Attribute Selectors
The attribute selectors allow you to specify elements based on their attributes and their values. The double quotes in the following may be omitted. Also note that we've just used the words "foo" and "bar" as placeholders and they may be replaced with any valid attribute name and value.
- E[foo] - matches an element E that has the attribute "foo" set to any value.
- E[foo="bar"] - matches an element E that has the attribute "foo" set exactly to "bar".
- E[foo~="bar"] - matches an element E whose attribute "foo" has one or more space-separated values, one of which is equal to "bar."
- E[foo^="bar"] - matches an element E whose attribute "foo" begins with "bar."
- E[foo$="bar"] - matches an element E whose attribute "foo" ends with "bar."
- E[foo*="bar"] - matches an element E whose attribute "foo" contains "bar."
- E[foo|="en"] - matches an element E whose attribute "foo" contains a hyphen-separated list of values that starts with "en". (This is a selector specifically for multilingual support.)
For example:
| This selector | Matches this... | But does not match this... |
| div[foo] | <div foo="bar" /> | <div bar="foo" /> |
| img[boo="bar"] | <img boo="bar" /> | <img boo="bare" /> |
| *[foo~="bar"] | <text foo="bar k" /> | <text foo="tree bark" /> |
| video[foot^="bar"] | <video foot="bark" /> | <video foot="crowbar" /> |
| text[foo$=bar] | <text foo="crowbar" /> | <text foo="crowbars" /> |
| div[foo*=bar] | <dev foo="abbarules" /> | <dev foo="batrust" /> |
| ="es"] | <text lang="es-MX" /> | <text lang="en-US" /> |
Pseudo-Class Selectors
"Pseudo-class" is the term CSS3 uses for a quality that may change on an element. For example, something you can click on has a quality of "link" until it's clicked -- then it no longer has that quality. Instead it has the quality "visited". Another example is the "hover" quality. When the mouse is rolled over an element, it has this quality but not when it's rolled off.
As mentioned, the link pseudo-classes allow you to set the look of a link before and after it's been visited.
- E:link - matches an element E that is a link that has not been visited.
- E:visited - matches an element E that is a link that has been visited.
The way to make something a link in Frontal is to use the 'is-link' style. Also, there are several style classes in Frontal's default style sheet that set this style. Here then is an example of the pseudo-classes 'link' and 'visited':
<style><![CDATA[ text:link { color: blue; } text:visited { color: red; } ]]></style> <text style="is-link: true;">click me</text>
Other similar pseudo-classes include
- E:selected - matches an element that is a managed element and is currently selected. (See the 'manager' tag section for more details.)
- E:disabled - matches an element that is disabled.
The user action pseudo-classes include:
- E:active - matches an element that has the mouse button pressed on it.
- E:hover - matches an element that has the mouse over it.
- E:focus - matches an element that has focus.
For example:
<style><![CDATA[ text:focus { color: green; } text:hover { color: red; } text:active { color: blue; } ]]></style> <text style="tab-enabled: true;">click me 1</text> <text style="tab-enabled: true;">click me 2</text>
Custom Pseudo-Class Selectors
Frontal expands on the CSS3 selectors standard with custom pseudo-class selectors. These selectors act very much like a normal pseudo-class but instead of acting on built-in states like 'hover' or 'active', they act on states that the user can define.
- E:myPseudoClass - matches an element E with a custom pseudo-class "myPseudoClass" that evaluates to true.
- E:myPseudoClass(value) - matches an element E with a custom pseudo-class "myPseudoClass" that is equal to value.
Here's an example where a button cycles through colors as it is clicked, and alternates between being underlined and not.
<style><![CDATA[ text { @onClick { if ( dynamic.count == null ) dynamic.count = 0; dynamic.count++; setPseudoClassState ( "state", dynamic.count % 3 ); setPseudoClassState ( "underlined", ( dynamic.count % 2 ) == 1 ); } } text:state(0) { color: green; } text:state(1) { color: red; } text:state(2) { color: blue; } text:underlined { underline: true; } ]]></style> <text>click me</text>
Also note that like any selector, it may be chained with others. For example, we can combine the hover pseudo-class with one of our custom ones:
text:state(2):hover { color: yellow; }
While this example is pretty arbitrary, we use it in Frontal to support sectioned sites and menus. In that case, custom pseudo-classes are used to indicate the hierarchical relationship between an element and the currently selected section. For more details, see the Details section.
Class Selectors
The class selector is very commonly used and is a special case of an attribute selector:
- E.styleClass - matches an element E whose attribute 'class' has one or more space-separated values, one of which is equal to 'styleClass'.
The reason this selector is popular is because its format is easy to remember, it's easy to read, and it's a convenient way to define common styles as groups that can then be applied in combination as needed. For example:
<style><![CDATA[ .green { color: green; } .red { color: red; } .underlined { underline: true; } ]]></style> <text class="green underlined">This text is green and underlined.</text> <text class="red">This text is red.</text> <text class="red underlined">This text is red and underlined.</text>
ID Selectors
Another popular selector is the ID selector. It's also a special case of an attribute selector:
- E#identifier - matches an element E while attribute 'id' has the value 'identifier'.
This selector is convenient because it allows styles and attributes to be applied to a particular element without needing to put all of those styles and attributes directly on the element. For example:
<style><![CDATA[ #textOne { color: green; underline: true; @onRollOver { save = text; text = 'Get off me!'; } @onRollOut { text = save; } } ]]></style> <text id="textOne">This text is green, underlined and doesn't like being rolled over.</text>
Negation Pseudo-Class
The negation pseudo class allows us to negate the result of a selector:
- E:not(S) - matches an element E that does not match the selector S.
For example, here we set the color of non-English text to silver.
<style><![CDATA[ text:not(:lang(en)) { color: silver; } ]]></style> <text lang="en-US">Hello, world.</text> <text lang="en-GB">'allo, world.</text> <text lang="fr">Bonjour, monde.</text> <text lang="es">Hola, mundiale.</text>
Creating Complex Selectors
The selector formats we've seen so far may be chained together to create more complex selectors. For example:
| This selector | Matches... |
| div#myDiv:hover | <div id="myDiv" /> when the mouse is rolled over it. |
| text.myLink:link:active | <text class="myLink" style="is-link: true;">click</text> when the mouse is pressed on it the first time. (After the first time, the text no longer matches the :link pseudo class.) |
| text:not(lang(en)):not(:hover) | <text lang="fr">Bonjour.</text> as long as the mouse is not over it. |
Combinators
Combinators allow the selection of elements based on the structure of the document. The combinators are:
- E F - matches an element F that is a descendant of E. A "descendant" is a child at any level, meaning child, grandchild, great grandchild, etc.
- E > F - matches an element F that is an immediate child of E.
- E + F - matches an element F that immediately follows an element E.
- E ~ F - matches an element F that follows (not just immediately) an element E.
Note that in this list, we've shown the combinators only operating on element selectors. In fact, they may act on any kind of selector. For example, here combinators are applied to element selectors, pseudo-class selectors, class selectors and id selectors:
<style><![CDATA[ div text:hover { underline : true; } div.top > text { color: red; } div.top text + text { color: blue; } text#text1 ~ text:hover { color: green; } ]]></style> <div class="top"> <div> <text>I'm underlined on roll over because of selector 1.</text> </div> <text id="text1">I'm underlined on roll over because of selector 1. I'm red from selector 2.</text> <text>I'm underlined on roll over because of selector 1. I'm blue from selector 3. I'm green on roll over from selector 4.</text> <div /> <text>I'm underlined on roll over because of selector 1. I'm green on roll over from selector 4.</text> </div> <text>I'm just plain old text. :(</text>
Differences from CSS3
While Frontal's style sheets approach has much in common with CSS3, and knowledge of one will be helpful in understanding the other, there are a few significant differences:
- Frontal does not support the pseudo-class selectors :enabled, :checked, ::before, ::after, ::first-line, ::first-letter or its structural pseudo-classes.
- Frontal's selectors are case sensitive.
- Frontal does not support the CSS3 "@" rules like @import and @media.
- Frontal does not support namespaces in selectors yet.
- Frontal supports user-definable, custom pseudo classes.
- Frontal supports attributes in the rule set declarations, e.g., @onClick.
- Frontal supports scripted actions whenever a selector matches or mismatches an element (see the Details section).
- Frontal supports rule set ids to allow for scripted interactions with selectors and the elements they match (see the Details section).
- Frontal supports comments (both the // and /* ... */ varieties) in its style sheets and rule sets.