RenoirST is a DSL enabling programmatic cascading style sheet generation for Pharo developed by Gabriel Omar Cotelli.
RenoirST aims to improve CSS integration with existing web frameworks. To do that, RenoirST generates CSS out of Pharo code. Renoir features are: common properties declaration, CSS3 selector support, important rules, font face rules and media queries support. In this tutorial we will present the key features of RenoirSt with a large set of examples. This tutorial assumes some knowledge of CSS and Pharo. For a little introduction about CSS you can read the Seaside's book CSS chapter.
To load the library in your image, evaluate:
Metacello new
configuration: 'RenoirSt';
githubUser: 'gcotelli' project: 'RenoirSt' commitish: 'master' path: 'source';
load
download a ready to use image from the Pharo contribution CI Server or install it using the Catalog/Configuration Browser.
The main entry point for the library is the class CascadingStyleSheetBuilder
. In a workspace or playground, inspect the result of the following expression:
CascadingStyleSheetBuilder new build
You now have an inspector on your first (empty and useless) style sheet. Real stylesheets are composed of rules (or rule-sets), where each one has a selector and a declaration group. The selector determines if the rule applies to some element in the DOM, and the declaration group specifies the style to apply.
Our first useful style sheet will simply assign a margin to every div element in the DOM.
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style | style margin: 2 px ];
build
the expected result in CSS is:
div
{
margin: 2px;
}
The message declareRuleSetFor:with:
is used to configure a rule-set in the builder. The message requires two blocks: the first block defines the selector
and the second defines the style to apply to elements matching the selector. The selector
argument of the first block is an entry point to construct the
selector (more on this later). The style
argument of the second block is an entry point to declare CSS properties and values.
The properties API is mostly defined following this rules:
margin:
message send.marginTop:
message send.We now present how the various CSS rules can be expressed with RenoirSt. RenoirSt supports many CSS types, comments, and even functional notation.
The library provides out-of-the-box support for the length, angle, time and frequency units in the CSS spec. There are extensions for Integer
and Float
classes allowing to obtain lengths.
The supported length units are:
em
relative to font sizeex
relative to "x" heightch
relative to width of the zero glyph in the element's fontrem
relative to font size of root elementvw
1% of viewport's widthvh
1% of viewport's heightvmin
1% of viewport's smaller dimensionvmax
1% of viewport's larger dimensioncm
centimetersmm
millimeteresin
inchespc
picaspt
pointspx
pixels (note that CSS has some special definition for pixel)The supported angle units are:
deg
degreesgrad
gradiansrad
radiansturn
turnsThe supported time units are:
s
secondsms
millisecondsThe supported frequency units are:
Hz
HertzkHz
KiloHertz
RenoirST also supports the creation of percentages: 50 percent
is mapped to 50%
in the resulting CSS.
Some properties require integer or floating point values. In these cases just use the standard Pharo integer and float support. For example:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style | style zIndex: 2 ];
build
The library supports abstractions for properties requiring color values. The shared pool CssSVGColors
provides easy access to colors in the SVG 1.0 list,
and the abstractions CssRGBColor
and CssHSLColor
allow the creation of colors in the RGB and HSL spaces including alpha support.
For example,
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style |
style
backgroundColor: CssSVGColors aliceBlue;
borderColor: (CssRGBColor red: 0 green: 128 blue: 0 alpha: 0.5) ];
build
evaluates to:
div
{
background-color: aliceblue;
border-color: rgba(0,128,0,0.5);
}
In a real scenario you should avoid hard coding colors as in the examples. It is recommended to put colors in objects representing a theme or something that gives them a name related to your application.
RGB-Colors also support percentage values:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style |
style borderColor: (CssRGBColor red: 0 percent green: 50 percent blue: 0 percent) ];
build
evaluates to:
div
{
border-color: rgb(0%,50%,0%);
}
Notice the difference in the used message because there is no alpha channel specification.
A lot of values for CSS properties are just keyword constants. This support is provided by the classes CssConstants
and CssFontConstants
.
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style | style textAlign: CssConstants justify ];
build
evaluates to:
div
{
text-align: justify;
}
Some properties support a wide range of values. For example the margin
property can have 1, 2 , 3 or 4 values specified. If you only need one value, just
pass it as a parameter. For more than one value, use an array:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style | style margin: { 2 px. 4 px } ];
build
evaluates to:
div
{
margin: 2px 4px;
}
ZnUrl
instances can be used as the value for properties requiring an URI. Both relative and absolute URLs are accepted. A relative URL is considered by default relative to the site root.
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div class: 'logo' ]
with: [ :style | style backgroundImage: 'images/logo.png' asZnUrl ];
declareRuleSetFor: [ :selector | selector div class: 'logo' ]
with: [ :style | style backgroundImage: 'http://www.example.com/images/logo.png' asZnUrl ];
build
Evaluates to:
div.logo
{
background-image: url("/images/logo.png");
}
div.logo
{
background-image: url("http://www.example.com/images/logo.png");
}
To use a URL relative to the style sheet, send to it the message relativeToStyleSheet
.
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div class: 'logo' ]
with: [ :style | style backgroundImage: 'images/logo.png' asZnUrl relativeToStyleSheet];
build
Evaluates to:
div.logo
{
background-image: url("images/logo.png");
}
When declaring rule sets, the library supports attaching comments to them with the declareRuleSetFor:with:andComment:
message:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style | style margin: 2 pc ]
andComment: 'Two picas margin';
build
evaluates to:
/*Two picas margin*/
div
{
margin: 2pc;
}
RenoirST also supports defining stand-alone comments (not attached to any rule):
CascadingStyleSheetBuilder new
comment: 'A general comment';
build
evaluates to:
/*A general comment*/
A functional notation is a type of CSS component value that can represent complex types or invoke special processing. Mathematical expressions, toggling between values, attribute references, and gradients are all supported in RenoirST.
The library provides support for math expressions using the CssMathExpression
abstraction:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style | style margin: (CssMathExpression on: 2 pc) / 3 + 2 percent ];
build
evaluates to:
div
{
margin: calc(2pc / 3 + 2%);
}
To let descendant elements cycle over a list of values instead of inheriting the same value, one can use the CssToggle
abstraction:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector unorderedList unorderedList ]
with: [ :style | style listStyleType: (CssToggle cyclingOver: { CssConstants disc. CssConstants circle. CssConstants square}) ];
build
evaluates to:
ul ul
{
list-style-type: toggle(disc, circle, square);
}
The attr()
function is allowed as a component value in properties applied to an element or pseudo-element. The function returns the value of an attribute on
the element. If used on a pseudo-element, it returns the value of the attribute on the pseudo-element's originating element. This function is supported using
the CssAttributeReference
abstraction and can be used simply providing an attribute name:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div before ]
with: [ :style | style content: (CssAttributeReference toAttributeNamed: 'title') ];
build
Evaluates to:
div::before
{
content: attr(title string);
}
RenoirST allows for providing the type or unit of the attribute (if no type or unit is specified the string
type is assumed):
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style |
style width: (CssAttributeReference
toAttributeNamed: 'height'
ofType: CssLengthUnits pixel) ];
build
evaluates to:
div
{
width: attr(height px);
}
Additionally, it is possible to provide a value to use when the attribute is absent:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div before ]
with: [ :style |
style content: (CssAttributeReference
toStringAttributeNamed: 'title'
withFallback: 'Missing title')
];
build
evaluates to:
div::before
{
content: attr(title string, "Missing title");
}
A gradient is an image that smoothly fades from one color to another. Gradients are commonly used for subtle shading in background images, buttons, and many
other places. The gradient notations described in this section allow an author to specify such an image in a terse syntax. This notation is supported using
CssLinearGradient
and CssRadialGradient
abstractions.
To represent a simple linear gradient from a color to another, send the fading:
message to CssLinearGradient
with the two colors in an array as a parameter:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style | style background: (CssLinearGradient
fading: { CssSVGColors yellow. CssSVGColors blue }) ]
The above code evaluates to:
div
{
background: linear-gradient(yellow, blue);
}
By default, a gradient's direction is from top to bottom. To specify a different direction, the author can use the to:fading:
message instead:
CssLinearGradient
to: CssConstants right
fading: { CssSVGColors yellow. CssSVGColors blue }
The above code will result in a gradient with yellow on the left side and blue on the right side. The equivalent CSS is:
linear-gradient(to right, yellow, blue);
To specify a diagonal direction, an array must be passed as the to:
argument:
CssLinearGradient
to: { CssConstants top. CssConstants right }
fading: { CssSVGColors yellow. CssSVGColors blue }
The above code will result in a gradient with blue in the top right corner and yellow in the bottom left one. The equivalent CSS is:
linear-gradient(to top right, yellow, blue);
Directions can also be specified as an angle by sending the rotated:fading:
message:
CssLinearGradient
rotated: 45 deg
fading: { CssSVGColors yellow. CssSVGColors blue }
The above code maps to:
linear-gradient(45deg, yellow, blue);
Gradients can be fine-tuned by manipulating so-called color stops:
CssLinearGradient
to: CssConstants right
fading: {
CssSVGColors yellow.
CssColorStop for: CssSVGColors blue at: 30 percent }
The above code maps to:
linear-gradient(to right, yellow, blue 30%);
This results in a linear gradient from left to right with yellow at the left side and plain blue from 30% (of the horizontal line) to the right side. More than
two colors can be passed as argument to fading:
. This can be used to create rainbows:
CssLinearGradient
to: CssConstants right
fading: {
CssSVGColors red.
CssSVGColors orange.
CssSVGColors yellow.
CssSVGColors green.
CssSVGColors blue.
CssSVGColors indigo.
CssSVGColors violet.
}
This maps to:
linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet);
To create radial gradients, the author must send messages to CssRadialGradient
. For example,
CssRadialGradient fading: { CssSVGColors yellow. CssSVGColors green }
maps to:
radial-gradient(yellow,green)
This results in a radial gradient with yellow at the center and green all around. Coordinates can be passed to both the first and second parameters of the
elliptical:at:fading:
message:
(CssRadialGradient
elliptical: {20 px. 30 px}
at: { 20 px. 30 px}
fading: { CssSVGColors red. CssSVGColors yellow. CssSVGColors green })
This maps to:
background: radial-gradient(20px 30px ellipse at 20px 30px, red, yellow, green);
To make the gradient repeatable, just send to it the message beRepeating
. For Example:
(CssRadialGradient fading: { CssSVGColors yellow. CssSVGColors green }) beRepeating
renders as:
repeating-radial-gradient(yellow, green);
Box Shadows are supported with CssBoxShadow
abstraction. This abstraction simplifies the use of the box-shadow
property.
CssBoxShadow
horizontalOffset: 64 px
verticalOffset: 64 px
blurRadius: 12 px
spreadDistance: 40 px
color: (CssSVGColors black newWithAlpha: 0.4)
evaluates to:
64px 64px 12px 40px rgba(0,0,0,0.4)
Several shadows can be combined:
(CssBoxShadow horizontalOffset: 64 px verticalOffset: 64 px blurRadius: 12 px spreadDistance: 40 px color: (CssSVGColors black newWithAlpha: 0.4)) ,
(CssBoxShadow horizontalOffset: 12 px verticalOffset: 11 px blurRadius: 0 px spreadDistance: 8 px color: (CssSVGColors black newWithAlpha: 0.4)) beInset
Evaluates to:
64px 64px 12px 40px rgba(0,0,0,0.4), inset 12px 11px 0px 8px rgba(0,0,0,0.4)
So far our focus was on the style part of the rule. Let's focus now on the available selectors. Remember that a CSS selector represents a structure used to match elements in the document tree. This chapter asume some familiarity with the CSS selectors and will not go in detail about the exact meaning of each one. For more details you can take a look at CSS3 selector documentation.
These selectors match a specific element type in the DOM. The library provides out-of-the-box support for HTML elements. One example is the div
selector used in the previous chapter:
CascadingStyleSheetBuilder new
declareRuleSetFor: [ :selector | selector div ]
with: [ :style | ... ];
build
The following other example matches <ol>
(ordered list) elements:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector orderedList ]
with: [ :style | ... ]
evaluating to:
ol
{
...
}
To get a list of the supported type selectors evaluate CssSelector selectorsInProtocol: '*RenoirSt-HTML'
.
Selectors can be combined to represent complex queries. One of the most common use cases is the descendant combinator:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector div orderedList ]
with: [:style | ... ]
evaluating to:
div ol
{
...
}
This only matches if an ol
element is a descendant (direct or not) of a div
element.
The child combinator only matches when an element is a direct child of another one:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector div > selector orderedList ]
with: [:style | ]
evaluates to
div > ol
{
...
}
Siblings combinators can be created using +
and ~
:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector div + selector orderedList ]
with: [:style | ]
declareRuleSetFor: [:selector | selector div ~ selector orderedList ]
with: [:style | ]
Class selectors can be created by sending class:
and id selectors can be created by sending id:
. For example,
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | (selector div class: 'pastoral') id: #account5 ]
with: [:style | ]
evaluates to:
div.pastoral#account5
{
...
}
You should not hardcode the classes and ids, they should be obtained from the same object that holds them for the HTML generation. You probably have some code setting the class(es) and/or id(s) to a particular HTML element.
A comma-separated list of selectors represents the union of all elements selected by each of the individual selectors in the list. For example, in CSS when several selectors share the same declarations, they may be grouped into a comma-separated list.
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector paragraph , selector div ]
with: [:style | ... ]
evaluates to:
p, div
{
...
}
Attribute selectors are useful to match an element based on its attributes and their values.
The attribute presence selector matches an element having an attribute (without considering the value of the attribute):
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector h1 havingAttribute: 'title' ]
with: [:style | style color: CssSVGColors blue ];
build
evaluates to:
h1[title]
{
color: blue;
}
The attribute value exact matching selectors matches an element having an attribute with a specific value:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector span withAttribute: 'class' equalTo: 'example' ]
with: [:style | style color: CssSVGColors blue ];
build
evaluates to:
span[class="example"]
{
color: blue;
}
The attribute value inclusion selector matches an element having an attribute with a value including as a word the matching term:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector anchor attribute: 'rel' includes: 'copyright' ]
with: [:style | style color: CssSVGColors blue ];
build
a[rel~="copyright"]
{
color: blue;
}
Other attribute selectors are used for substring matching:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector anchor firstValueOfAttribute: 'hreflang' beginsWith: 'en' ]
with: [:style | style color: CssSVGColors blue ];
build
a[hreflang|="en"]
{
color: blue;
}
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector anchor attribute: 'type' beginsWith: 'image/' ]
with: [:style | style color: CssSVGColors blue ];
build
a[type^="image/"]
{
color: blue;
}
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector anchor attribute: 'type' endsWith: '.html' ]
with: [:style | style color: CssSVGColors blue ];
build
a[type$=".html"]
{
color: blue;
}
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector paragraph attribute: 'title' includesSubstring: 'hello' ]
with: [:style | style color: CssSVGColors blue ];
build
p[title*="hello"]
{
color: blue;
}
The pseudo-class concept is introduced to allow selection based on information that lies outside of the document tree or that cannot be expressed using the simpler selectors. Most pseudo-classes are supported just by sending one of the following messages link
, visited
, active
, hover
, focus
, target
, enabled
, disabled
or checked
.
Here is a small example that uses the pseudo-classes:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector anchor link ]
with: [:style | style color: CssSVGColors blue ];
declareRuleSetFor: [:selector | selector anchor visited active]
with: [:style | style color: CssSVGColors green ];
declareRuleSetFor: [:selector | selector anchor focus hover enabled]
with: [:style | style color: CssSVGColors green ];
declareRuleSetFor: [:selector | (selector paragraph class: 'note') target disabled]
with: [:style | style color: CssSVGColors green ];
declareRuleSetFor: [:selector | selector input checked ]
with: [:style | style color: CssSVGColors green ];
build
evaluates to:
a:link
{
color: blue;
}
a:visited:active
{
color: green;
}
a:focus:hover:enabled
{
color: green;
}
p.note:target:disabled
{
color: green;
}
input:checked
{
color: green;
}
The :lang(C)
pseudo-class can be used by sending the message lang:
:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | (selector lang: 'es') > selector div ]
with: [:style | style quotes: { '"«"'. '"»"' } ];
build
evaluates to:
:lang(es) > div
{
quotes: "«" "»";
}
The negation pseudo-class, :not(X)
, is a functional notation taking a simple selector (excluding the negation pseudo-class itself) as an argument. It represents an element that is not represented by its argument. For more information take a look at the CSS spec.
This selector is supported sending the message not:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector button not: (selector havingAttribute: 'DISABLED') ]
with: [:style | style color: CssSVGColors blue ];
build
button:not([DISABLED])
{
color: blue;
}
These selectors allow selection based on extra information that lies in the document tree but cannot be represented by other simpler selectors nor combinators.
The :root
pseudo-class represents an element that is the root of the document. To build this kind of selector just send the message root
to another selector:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector root ]
with: [:style | style color: CssSVGColors grey ];
build
evaluates to:
:root
{
color: grey;
}
The :nth-child(an+b)
pseudo-class notation represents an element that has an+b-1
siblings before it in the document tree, for any positive integer or
zero value of n
, and has a parent element. For values of a
and b
greater than zero, this effectively divides the element's children into groups of a
elements (the last group taking the remainder), and selecting the b
th element of each group. The a
and b
values must be integers (positive, negative, or zero). The index of the first child of an element is 1.
In addition to this, :nth-child()
can take a number, odd
and even
as arguments. The value odd
is equivalent to 2n+1
, whereas even
is equivalent to 2n
.
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector childAt: 3 n + 1 ]
with: [:style | style color: CssSVGColors blue ];
declareRuleSetFor: [:selector | selector childAt: 5 ]
with: [:style | style color: CssSVGColors blue ];
declareRuleSetFor: [:selector | selector childAt: CssConstants even]
with: [:style | style color: CssSVGColors blue ];
build
evaluates to:
:nth-child(3n+1)
{
color: blue;
}
:nth-child(5)
{
color: blue;
}
:nth-child(even)
{
color: blue;
}
All structural pseudo-classes can be generated using the following messages:
CSS pseudo-class | RenoirST selector message |
---|---|
root() |
root |
nth-child() |
childAt: |
nth-last-child() |
childFromLastAt: |
nth-of-type() |
siblingOfTypeAt: |
nth-last-of-type() |
siblingOfTypeFromLastAt: |
first-child |
firstChild |
last-child |
lastChild |
first-of-type |
firstOfType |
last-of-type |
lastOfType |
only-child |
onlyChild |
only-of-type |
onlyOfType |
empty |
empty |
Pseudo-elements create abstractions about the document tree beyond those specified by the document language. For instance, document languages do not offer mechanisms to access the first letter or first line of an element's content. Pseudo-elements allow authors to refer to this otherwise inaccessible information. Pseudo-elements may also provide authors a way to refer to content that does not exist in the source document.
The firstLine
pseudo-element describes the contents of the first formatted line of an element.
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector paragraph firstLine ]
with: [:style | style textTransform: CssConstants uppercase ];
build
evaluates to:
p::first-line
{
text-transform: uppercase;
}
The firstLetter
pseudo-element represents the first letter of an element, if it is not preceded by any other content (such as images or inline tables) on its line.
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector paragraph firstLetter ]
with: [:style | style fontSize: 200 percent ];
build
evaluates to:
p::first-letter
{
font-size: 200%;
}
The before
and after
pseudo-elements can be used to describe generated content before or after an element's content. The content
property, in conjunction with these pseudo-elements, specifies what is inserted.
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | (selector paragraph class: 'note') before ]
with: [:style | style content: '"Note: "' ];
declareRuleSetFor: [:selector | (selector paragraph class: 'note') after ]
with: [:style | style content: '"[*]"' ];
build
evaluates to:
p.note::before
{
content: "Note: ";
}
p.note::after
{
content: "[*]";
}
CSS attempts to create a balance of power between author and user style sheets. By default, rules in an author's style sheet override those in a user's style sheet.
However, for balance, an !important
declaration takes precedence over a normal declaration. Both author and user style sheets may contain !important
declarations, and user !important
rules override author !important
rules. This CSS feature improves accessibility of documents by giving users with
special requirements control over presentation.
RenoirSt supports this feature through the beImportantDuring:
message sent to the style.
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector paragraph ]
with: [:style |
style beImportantDuring: [:importantStyle |
importantStyle
textIndent: 1 em;
fontStyle: CssConstants italic ].
style fontSize: 18 pt ];
build
evaluates to:
p
{
text-indent: 1em !important;
font-style: italic !important;
font-size: 18pt;
}
Note that the important properties must be created by sending the messages to the inner argument importantStyle
instead of the outer argument style
.
A @media
rule specifies the target media types of a set of statements. The @media
construct allows style sheet rules that apply to various media in the
same style sheet. Style rules outside of @media
rules apply to all media types that the style sheet applies to. At-rules inside @media
are invalid in
CSS2.1.
The most basic media rule consists of specifying just a media type:
CascadingStyleSheetBuilder new
declare: [ :cssBuilder |
cssBuilder
declareRuleSetFor: [ :selector | selector div ]
with: [ :style | ] ]
forMediaMatching: [ :queryBuilder |
queryBuilder type: CssMediaQueryConstants print ];
build
evaluates to:
@media print
{
div { }
}
To use media queries in the library just send the message declare:forMediaMatching:
to the builder. The first block is evaluated with an instance of a
CascadingStyleSheetBuilder
and the second one with a builder of media queries.
The media query builder will match any media type by default. To specify a media type just send it the message type:
with the corresponding media type. The
class CssMediaQueryConstants
provides easy access to the following media types:
braille
, embossed
, handheld
, print
, projection
, screen
, speech
, tty
and tv
.
The media query builder supports a variety of messages for additional conditions (called media features). Media features are used in expressions to describe requirements of the output device.
The following media feature messages are supported:
CssMeasure
with length units: width:
, minWidth:
, maxWidth:
, height:
, minHeight:
, maxHeight:
, deviceWidth:
, minDeviceWidth:
, maxDeviceWidth:
, deviceHeight:
, minDeviceHeight:
, maxDeviceHeight:
;orientation:
accepting CssMediaQueryConstants portrait
or CssMediaQueryConstants landscape
;aspectRatio:
, minAspectRatio:
, maxAspectRatio:
, deviceAspectRatio:
, minDeviceAspectRatio:
, maxDeviceAspectRatio:
;color:
(the argument describes the number of bits per color component of the output device), minColor:
, maxColor:
, colorIndex:
(the argument describes the number of entries in the color lookup table of the output device), minColorIndex:
, maxColorIndex:
, monochrome:
(the argument describes the number of bits per pixel in a monochrome frame buffer), minMonochrome:
, maxMonochrome:
, grid:
(the argument must be 1 or 0);CssMeasure
with resolution units: resolution:
, minResolution:
, maxResolution:
;scan:
accepting CssMediaQueryConstants progressive
or CssMediaQueryConstants interlace
.
New units for resolutions are added using the CssMeasure
abstraction. This kind of measures can be created sending the messages dpi
(dots per inch),
dpcm
(dots per centimeter) or dppx
(dots per pixel unit) to an integer or float.
Here is a final example to better understand the media features support:
CascadingStyleSheetBuilder new
declare: [ :cssBuilder |
cssBuilder
declareRuleSetFor: [ :selector | selector id: #oop ]
with: [ :style | style color: CssSVGColors red ] ]
forMediaMatching: [ :queryBuilder |
queryBuilder
orientation: CssMediaQueryConstants landscape;
resolution: 300 dpi ];
build
evaluates to:
@media all and (orientation: landscape) and (resolution: 300dpi)
{
#oop
{
color: red;
}
}
The library doesn't provide out of the box support for non standard properties. Nevertheless, the message vendorPropertyAt:put:
is available to ease the creation of this kind of properties by the end user:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector div ]
with: [:style | style vendorPropertyAt: 'crazy-margin' put: 1 px ];
build
evaluates to:
div
{
crazy-margin: 1px;
-moz-crazy-margin: 1px;
-webkit-crazy-margin: 1px;
-o-crazy-margin: 1px;
-ms-crazy-margin: 1px;
}
If you really want to use a vendor specific extension, It's better to create an extension method sending the vendorPropertyAt:put:
message.
The @font-face
rule allows for linking to fonts that are automatically fetched and activated when needed. This allows authors to select a font that closely matches the design goals for a given page rather than limiting the font choice to a set of fonts available on a given platform. A set of font descriptors define the location of a font resource, either locally or externally, along with the style characteristics of an individual face.
This support is implemented in the builder:
CascadingStyleSheetBuilder new
declareFontFaceRuleWith: [ :style |
style
fontFamily: 'Gentium';
src: 'http://example.com/fonts/gentium.woff' asZnUrl ];
build
evaluates to:
@font-face
{
font-family: Gentium;
src: url("http://example.com/fonts/gentium.woff");
}
This kind of rule allows for multiple src
definitions specifying the resources containing the data. This resources can be external (fonts fetched from a URL) or local (available in the user system). This kind of resources are supported using CssLocalFontReference
and CssExternalFontReference
abstractions:
CascadingStyleSheetBuilder new
declareFontFaceRuleWith: [ :style |
style
fontFamily: 'MainText';
src: (CssExternalFontReference
locatedAt: 'gentium.eat' asZnUrl relativeToStyleSheet);
src: (CssLocalFontReference toFontNamed: 'Gentium'),
(CssExternalFontReference locatedAt: 'gentium.woff' asZnUrl relativeToStyleSheet withFormat: CssFontConstants woff);
src: (CssExternalFontReference svgFontLocatedAt: 'fonts.svg' asZnUrl relativeToStyleSheet withId: 'simple') ];
build
@font-face
{
font-family: MainText;
src: url("gentium.eat");
src: local(Gentium), url("gentium.woff") format("woff");
src: url("fonts.svg#simple") format("svg");
}
The Units
package (available using the ConfigurationBrowser in Pharo) includes some extensions colliding with RenoirSt. RenoirST can automatically load a compatibility package if it's loaded after the Units
package. To test this integration there's a
specific continuous integration job, that loads Units
first and then RenoirSt
.
RenoirSt includes an optional group including some useful extensions. The Seaside framework includes its own class modeling URLs: when this group is loaded the instances of WAUrl
can be used in the properties requiring an URI:
CascadingStyleSheetBuilder new
declareRuleSetFor: [:selector | selector div class: 'logo' ]
with: [:style |
style backgroundImage: 'images/logo.png' seasideUrl ];
build
evaluates to:
div.logo
{
background-image: url("/images/logo.png");
}
This optional group also loads extensions to CssDeclarationBlock
so it can be used as a JSObject
in plugins requiring some style parameter or as the argument in a component style:
method.
To load these extensions in an image with Seaside already loaded, you need to load the group Deployment-Seaside-Extensions
, or Development-Seaside-Extensions
if you want the test cases (there is also a stable-pharo-40
branch if needed):
Metacello new
baseline: 'RenoirSt';
repository: 'github://gcotelli/RenoirSt:stable-pharo-50/source';
load: 'Deployment-Seaside-Extensions'