CSS Within introduces css:rule
and
css:media
elements into XSLT stylesheets. We will
begin by looking at css:rule
since it is the
fundamental building block both of CSS Within and, indirectly, of
CSS itself.
Consider an XSLT template as follows:
<xsl:template match="products-found"> <!--* match search results, if any *--> <div class="productlist"> <xsl:on-non-empty> <h3>Products</h3> </xsl:on-non-empty> <xsl:apply-templates/> <xsl:on-empty> <p class="no-products"> No products matched your irritating query. Go away. </p> </xsl:on-empty> <div> </xsl:template>
Styling the Products heading might require CSS like the following:
div.productlist { border: 1px solid grey; padding: 1rem; } div.productlist>h3 { font-family: "Bland Sans", sans; border-top: 1px dotted grey; }
This CSS fragment consists of two rules, each consisting of a
selector followed by a group of rules
contained within {
curly brackets }
. The
first rule has a selector that says the rule applies to any
div
element whose class
attribute
contains the token productlist
. The second rule has a
selector that applies to every h3
element whose
immediate parent is such a div
element. Assuming no
other more specific or subsequent rule overrides these, the rules
assign values to various CSS properties such as
padding
and font-family
. Beyond this,
the details of CSS are not important to CSS Within. However, it
is worth noting that CSS uses a text-based
non-XML syntax with which people who work with Web development are
very familiar.
It is clear from this example that the link between the stylesheets and the HTML being constructed is fragile: if the generated element structure is changed, or the class names are updated, the original CSS selectors will no longer match. One way to mitigate this is to put the CSS rules right next to the place where the elements they govern are generated. To put the CSS within the template, we might combine them as follows:
<xsl:template match="products-found"> <!--* match search results, if any *--> <div class="productlist"> <css:rule match="div.productlist"> border: 1px solid grey; padding: 1rem; </css:rule> <xsl:on-non-empty> <css:rule match="div.productlist>h3"> font-family: "Bland Sans", sans; border-top: 1px dotted grey; </css:rule> <h3>Products</h3> </xsl:on-non-empty> <xsl:apply-templates/> <xsl:on-empty> <p class="no-products"> No products matched your irritating query. Go away. </p> </xsl:on-empty> <div> </xsl:template>
The CSS Within tool set includes XSLT that will read the
stylesheet itself, extract all of the css:rule
elements
and write a CSS stylesheet file. In this case the CSS will be
identical to that shown above.
What have we gained? First, we no longer have a conflict of
syntax: there are no more curly braces and everything is in XML.
Second, the CSS definitions are right next to the elements they
style. It is easy to imagine changing the h3
to an
h4
but forgetting to change the CSS file; even if
we remember, we then have to open the CCSS file and
find the right rule to change. But now the
styles and the markup are in the same place we are likely to
remember and, remembering, will of course find it easy to locate the
style rule to update.
Sometimes you may generate the same structure from multiple places
in your stylesheet, but of course you don't want to repeat the CSS
rules in the generated stylesheet. In this case you can use an
empty css:rule
element and give it a ref
attribute whose value matches the name
attribute of
another css:rule
element. The name
attribute on the css:rule
element also serves as a
reminder that the style might be used elsewhere, helping to avoid
the situation where you accidentally delete a rule you thought you
no longer needed.
If you are using CSS for both print and screen, or if you have
stylesheets loaded only conditionally based on a media query (for
example, containing extra rules for wide screens or overriding
defaults set for circular displays such as on some wristwatches),
you may well need to write out more than one CSS file with XSLT. In
this case you can give css:rule
elements a
stream
attribute, and the contents will only be
included in the CSS ruleset you name. That way all the styles to do
with a given output element are together and as easy as possible to
update together, even if they are written out separately.
The way the CSS is written to files is described in a subsequent section in this paper.
If you have media queries in your stylesheet, you can use
css:media
elements to generate them; these elements
contain css:rule
elements:
<css:media when="min-width: 600px"> <css:rule match="ul.letterindex"> column-count: 2; </css:rule> </css:media> <css:media when="min-width: 800px"> <css:rule match="ul.letterindex"> column-count: 3; </css:rule> </css:media>