W3C Note, 11-Nov-1998
This document has been submitted to the World Wide Web Consortium (see Submission Request, W3C staff comment).
This document is a NOTE made available by W3C for discussion only. This indicates no endorsement of its content, nor that W3C has, is, or will be allocating any resources to the issues addressed by the NOTE.
The release of Cascading Style Sheets in the World Wide Web universe the 17 december 1996 has been a major evolution of Web publishing. For the first time in very common software tools, it is possible to separate content and presentation in data. This old dream of SGML gurus, fighting against the rest of the world to make people realize this is important, is now a reality. Each HTML element can carry presentation styles and forget presentation HTML attributes..
The main effect of this evolution is the deprecation of several HTML elements and attributes. It is then necessary to describe the set of transformations that should be applied to a HTML document containing these deprecated items in order to make it conformant to HTML Clean and take advantage of CSS.
Furthermore, there is a solution to manipulate the contents of a document as an author may need it, such as automatic building of a table of contents. Very simple content generation operations are possible using W3C selectors in a new way described in this specification.
The current specification does not want to re-invent the wheel and it should not be extended to a very large set of operations. The goal of the current specification is only to provide a simple and quick implementation of a transformation algorithm of documents that a HTTP server or a site management tool could for instance handle on the fly .
STTS use the core CSS syntax, while extending the CSS selectors. There is no restriction on CSS existing selectors.
The main change between STTS 2 and STTS 3 is related to selectors. While STTS 2 use them only in order to describe conditions, STTS 3 use them also for fragment descriptions. Main other changes :
A STTS transformations sheet is made of rules. A rule is itself made of two parts : conditions and a block of declarations .
Declarations contained in a STTS rule are applied to an element if the context of this element makes true one of the conditions attached to the rule.
A STTS sheet can contain comments that should be ignored.
Error handling rules are the same than in CSS.
A comment begins with a /*
and ends with */
.
Initial terminator must not be contained in character string surrounded by quotes or double quotes. Otherwise, comments can be placed anywhere in a STTS sheet but in the middle of an identifier. Tools must ignore comment terminators and the sequence of characters between these terminators. A comment should not be considered as a space.
A condition is a conditional link between the declarations of the STTS rule containing this link and the tree representing the document the rule is applied to.
A condition is a selector or a group of selectors. STTS are based on CSS 2 selectors (without pseudo-elements) and extended by the current specification.
A selector represents a true condition if all simple selectors and combinators comprising this selector represent themselves a true condition.
A fragment description is a description of a HTML or XML subtree, including attributes and textual content, based on selectors' syntax.
A fragment description is made of one selector, that can contain one or more sequences of simple selectors, separated by combinators. Groups of selectors are not allowed in fragment descriptions.
The following list is derived from first XSL public draft.
:first-of-type
:not-first-of-type
:last-of-type
:not-last-of-type
:first-of-any
:first-child
.:not-first-of-any
last-of-any
:not-last-of-any
:only-of-type
:not-only-of-type
:only-of-any
:not-only-of-any
An indirect adjacent combinator is described by two sequences of simple selectors,
separated by the combinator -
. It represents two siblings, the
second one (represented by the second sequence of simple selectors) occurring
after the first one (represented by the first sequence of simple selectors)
in the list of children of their parent element. For instance, the condition
:
EM - TT
is true for all TT
elements following a EM
in the
list of children of their common parent element.
Warning : indirect adjacent combinators are not allowed in fragment descriptions.
If document's subtree matches a condition, the element selected by the condition is by default the element of the subtree represented by the last sequence of simple selectors in the condition. If one sequence of simple selectors in the condition contains a selected element selector, the selected element is the subtree's element represented by this sequence.
The selected element selector is described by a pseudo-element functional notation
:selected()
which can be called without argument or with a condition
or group of conditions passed as argument.
If there is no argument, this notation represents the selected element on which
the declarations of the rule apply. :selected()
can be used in
conditions and fragment descriptions. For instance :
AUTHOR:selected() > FIRSTNAME { .... }
H1 { modify-context : HR + :selected() + HR }
Declarations contained in the first rule apply on all AUTHOR
which
contain a FIRSTNAME
. The second rule adds a HR
just
before and just after all H1
, these HR
and H1
having the same parent element.
If a condition or a group of condition is passed to this notation contained
in a fragment description, it defers the application of the declaration to all
elements matching the conditions passed to :selected()
. For instance
SPAN.classified {
insert-inside-first : *:selected(H1,H2,H3,H4) > IMG[SRC="classfd.gif"]
}
This rule adds the IMAGE
classfd.gif as the first sibiling
of all H1
, H2
, H3
and H4
for each SPAN
carrying class classified
in the document.
The SPAN
itself is not modified at all.
Only one selected element selector is allowed per condition or fragment description. More than one should be considered as an error.
A content selector represents a textual content of an element. Such a selector
is described by a character string surrounded by double brackets [[ ...
]]
. Between the double brackets is a succession of space-separated values
:
In a condition, it is a true/false test on the textual content of the selected
element. The test is true if the selected character string is a substring of
the concatenation of all #PCDATA
contained in the subtree this
selected element is the root of. For instance :
P[["hello"]] { ... }
will be applied to paragraphs P
containing substring hello
.
For instance, it will match:
<P>The teacher came in the room and said <STRONG>hello</STRONG> like every day.</P>
The character set used in content simple selectors is Unicode.
Warning : content selectors are not allowed in fragment descriptions.
Explosive descriptors are only allowed in fragment descriptions. An explosive
selector always precedes a simple selector. No character is allowed between
the explosive descriptor and its simple selector. An explosive selector modifies
the meaning of the attached simple selector. Explosive descriptors are indicated
by a circumflex accent ^
.
^ followed by |
meaning |
---|---|
a class selector | remove this class from the classes carried by the element |
an attribute existence selector | remove this attribute from attributes carried by the element |
a type element selector or universal selector | replaces the element by its contents |
Example 1 :
P[ALIGN="center"] { modify-context : ^[align].centered }
removes the ALIGN
attribute from all paragraphs that carry it
and adds the centered
class to these paragraphs.
Example 2 :
SPAN.warning { modify-context : .important^.warning }
Adds class important
to all SPAN
carrying class warning
.
Also removes class warning
.
Example 3 :
P > CENTER { modify-context : ^*:selected() }
applied to
<P><CENTER>bla <EM>bla</EM> bla</CENTER></P>
will result in
<P>bla <EM>bla</EM> bla </P>
Warning : this operation is performed regardless
to the DTD.
Conditions opening on the same declarative section can be grouped in a single rule. Conditions are then separated by commas.
For instance
DIV P.abstract { modify-context : .centered } H2 { modify-context : .centered } TABLE#b12 { modify-context : .centered }
and
DIV P.abstract, H2, TABLE#b12 { modify-context : .centered }
are equivalent because the three selectors composing the group are valid selectors.
A group of conditions can also be passed as argument of a selected element selector.
The declarative section (the block) of a rule follows conditions and is surrounded by curly braces.
{ ... }
A declaration is made of a property and a value
for this property, separated by a colon :
. A property defines
a transformation of the selected element or of the context of the selected element.
If the declarative section contains more than one declaration, they are separated
by semi-colons ;
.
Important : order of declarations matters : first read, first applied.
In a STTS rule, property values can access to the values of all attributes
carried by the selected element. These values are available through a call to
the functional notation attr(X)
where X
represents
the name of the attribute.
A second argument can be passed to attr()
. If
attr()
has two arguments, it retrieves the value of the attribute
carried by the first element in the document matching the condition being the
second argument.
Warning : if the element does
not explicitly carry an ID
, a call to attr(ID)
or
attr(ID, ...)
in order to retrieve the value of this attribute
should reply a unique ID generated by the STTS parser and given to the element.
Another functional notation is available :content()
retrieves
the concatenation of all #PCDATA
in the subtree the target element
is the root of. A selector can be passed as an argument to content()
.
If content()
has an argument, it retrieves the concatenation of
all #PCDATA
in the subtree the first element in the document matching
the selector given as argument is the root of.
The second argument of attr(x,y)
and content(x,y)
can also be one of the following notations :
ancestor(n)
which represents the n-th ancestor
of the selected element; a value of 0 replies the selected element itself.
parent()
represents the parent of the selected element and is
exactly equivalent to ancestor(1)
.previous(n)
which represents the n-th predecessor of the selected
element; a value of 0 replies the selected element itself. previous()
represents the direct predecessor of the selected element and is exactly equivalent
to previous(1)
.next(n)
which represents the n-th successor of the selected
element; a value of 0 replies the selected element itself. next()
represents the direct successor of the selected element and is exactly equivalent
to next(1)
.For instance :
BODY { modify-context : ^[BGCOLOR]^[TEXT] ; add-style : "background-color : " attr(bgcolor) ; add-style : "color : " attr(text) ; } A { modify-context : [TITLE=content()] }
All STTS properties defined in this document show a table like the following one :
Value | possible values or possible types of values |
Applies to | elements this property applies to |
Value | [ <string> | attr(X) | attr(X,selector) | parent()
| ancestor(n) | content() | content(selector) ]+ |
Applies to | all elements |
This property adds the STYLE
attribute to the target element if
necessary. It adds the value of the property to the contents of this attribute.
If the attribute was not empty, a semi-colon is appended to the initial content
before appending the value.
For instance :
H1[ALIGN] { modify-element : ^[ALIGN] ; add-style : "text-align : " attr(align) }
applied to
<H1 ALIGN=CENTER>Chapter 1</H1>
will result in
<H1 STYLE="text-align : CENTER">Chapter 1</H1>
Value | value is made of two or three data :
|
Applies to | HEAD only
|
As the application of CSS rules to XML documents is still being discussed at the W3C, the application of this property is restricted to HTML documents for the moment.
This property adds to the contents of the selected element a new LINK
to a CSS style sheet or a new STYLE
containing the contents of
the style sheet for the given media.
For instance :
HEAD { add-css : link url(http://www.edf.fr/styles/default.css) visual, aural }
applied to
... <HEAD> <TITLE>title of this document</TITLE></HEAD> ...
will result in
... <HEAD> <TITLE>title of this document</TITLE> <LINK REL=stylesheet TYPE="text/css" HREF="http://www.edf.fr/styles/default.css" MEDIA="visual,aural"> </HEAD> ...
Value | [ <string> | attr(X) | attr(X,selector) | parent()
| ancestor(n) | content() | content(selector) ]+ |
Applicable à | all elements |
The application of this property is restricted to HTML documents for the moment.
This property adds to the current document, in the last STYLE
element in HEAD
document's header or a new STYLE
element
created in HEAD
, the given CSS rule. If this rule is added to an
existing STYLE
element, the value of its MEDIA
attribute
should be exactly the given list of media ; otherwise, a new STYLE
should be created.
If the rule's definition contains double-quotes "
, these
quotes have to be preceeded by a backslash \
.
For instance :
BODY[LINK] { modify-context : ^[LINK] ; add-rule : "A:LINK { color : " attr(link) " } " }
Important warning : this rule will be added each
time conditions are matching an element in the HTML tree. Replace add-rule
by add-unique-rule
if you want this declaration to be applied only
once over the whole document. For instance, if you want to define a CSS rule
only if a certain CLASS
exists in the document :
.thisClass { add-unique-rule : ".otherClass { color : red }" }
Note : factorization of CSS rules if they have the same selector,
and factorization of CSS rules having the same declarative part but different
selectors is a more complex job, but implementable. The way add-rule
and add-unique-rule
add CSS rules to the document depends only
on the implementation of the User Agent.
Value | a fragment description |
Applies to | all elements |
It is possible to completely modify the context of the selected element of a rule adding attributes, inserting content inside, before or after the element, encapsulating the element in another one, ...
The value of this property describes the modified context of the selected element.
Warning : this operation is performed regardless of the DTD.
For instance :
P[ALIGN="CENTER"] { modify-context : ^[ALIGN].centered }
removes the ALIGN
attribute of P
paragraphs when its value is CENTER
and adds class centered
.
Selectors in the fragment description have the following effect on the selected element :
=
) adds the given attribute
to the context of the target element; the value of this attribute being the
given value,~=
) adds a whitespace
and the given value to the existing values of the given attribute carried
by the target element,#PCDATA
to the contents
of the target element,Examples :
CENTER { modify-context : DIV.centered }
Sequences of selectors at the left of the selected element in the fragment description indicate elements that must be created and inserted as ancestors or predecessors of the selected element.
Allowed selectors have here the following meaning :
=
) adds the given attribute
to the context of the element; the value of this attribute being the given
value,~=
) adds a white space
and the given value to the existing values of the given attribute carried
by the element,#PCDATA
to the contents
of the element,Examples :
P.abstract { modify-context : DIV > P.centered.emphasize[["Abstract"]] + :selected() } SPAN.glossaryEntry { modify-context : A[HREF="#" content()] > :selected() }
Sequences of selectors at the right of the selected element in the fragment description indicate elements that must be created and inserted as descendants or successors of the selected element.
Selectors have here the same meaning than in the previous section. Pseudo-classes
:first-child
and :last-child
are here allowed in the
second sequence of simple selectors of a child combinator if the first one describes
the selected element. It then indicates where should be inserted the second
element of the combinator.
Example :
P.warning { modify-context : :selected() > SPAN.strong[["WARNING !"]]:first-child + BR }
Five properties are specially meant for element insertion as shortcuts to modify-context
when its value is only composed of one fragment description.
Value | a fragment description without selected element selector |
Applies to | all elements |
insert-before : xxxx
modify-context : xxxx + :selected()
insert-after : xxxx
modify-context : :selected() + xxxx
insert-inside-first : xxxx
modify-context : :selected() > xxxx:first-child
insert-inside-last : xxxx
modify-context : :selected() > xxxx:last-child
encapsulate-in : xxxx
modify-context : xxxx > :selected()
STTS rules applied to a document are always applied before CSS and CAS rules. STTS rules do not modify the document itself but modify the internal view of the document in a conformant software.
STTS application to XML is unresolved.
Example 1 : dynamic association of a class to a language
[LANG] { modify-context : *[CLASS~=attr(LANG) "Styles"] }
applied to
<H1 LANG=en>Title of this section</H1>
will result in
<H1 LANG=en CLASS=enStyles>Title of this section</H1>
Example 2 : deprecation of all CENTER
elements in favor of DIV
elements carrying the ALIGN
attribute (being itself deprecated by HTML 4.0...)
CENTER { modify-context : DIV[ALIGN=CENTER] }
deprecation of all CENTER
elements in favor of DIV
elements carrying style text-align : center
.
CENTER { modify-context : DIV ; add-style : "text-align : center" }
Example 3 : insert "Section : " before
the contents of all H1
:
H1 { insert-inside-first : [["Section : "]] }
Example 4 : to add a legend after all images carrying
class standalone
, a legend being a centered paragraph carrying
class legend
and displaying the URL of the refered image
IMG.standalone { insert-after : P.legend[["URL : " attr(src)]] ; add-unique-rule : "P.legend { text-justify : center }" }
applied to :
<IMG CLASS=standalone SRC="http://www.edf.fr/logo.gif">
will result in :
<STYLE TYPE="text/css"> P.legend { text-justify : center } </STYLE> ... <IMG CLASS=standalone HREF="http://www.edf.fr/logo.gif"> <P CLASS=legend>URL : http://www.edf.fr/logo.gif</P>
Example 5 : automatic generation of a table of contents (level 1 sections only) at the end of the document, each item of the list being hyperlink to the refered section
BODY { insert-inside-last : HR + H1[["Table of contents"]] + OL.TDM } H1 { insert-inside-first : A[NAME="ref" attr(ID)] ; modify-context : :selected(OL.TDM) > LI:last-child > A[HREF="#ref" attr(ID)][[content()]] }
Example 6 : automatic generation of a table of
contents (level 1 and 2 sections) just after a DIV
with ID abstract
.
Level 1 sections are DIV
elements carrying class sec1
and containing all the contents of the section, including subsections. Level
1 section titles are H1
included in the DIV
. Level
2 section titles are H2
.
/* add a HR and then an ordered list with class */ /* TDM ; it will contain the table of contents */ DIV#abstract { insert-after : HR + H1[["Table of contents"]] + OL.TDM } /* for each H1, add an item to the list */ /* plus an empty list of subsections*/ DIV.sec1 > H1 { modify-context : :selected(OL.TDM) > LI[[content()]] + OL } /* for each H2, add an item to the last created sublist */ DIV.sec1 > H1 - H2 { modify-context : :selected(OL.TDM > LI:last-child > OL) > LI[[content()]] }
Example 7 : all images in the
document are thumbnails. Hyperlinks from these icons to the real size images
should be automatically added. Icons are in the document's directory but real
size images are located in the photos
subdirectory.
IMG { encapsulate-in : A[HREF="photos/" attr(SRC)] }
Example 8 : deprecation of HTML 3.2 structures in favor of CSS
Here is an example of a very simple set of STTS rules one can apply to a HTML 3.2 document in order to replace some deprecated attributes and elements by CSS styles. Warning : this set is not complete and is provided as is without any guarantee of any kind !
XMP, PLAINTEXT, LISTING { modify-context : PRE } CENTER { modify-context : DIV.centered } TT { modify-context : SPAN.monospace } I { modify-context : SPAN.italicized } DIR, MENU { modify-context : UL } BODY {
add-style : "background-color : " attr(bgcolor) ; add-style : "color : " attr(text) ; add-unique-rule : "A:link { color : " attr(link) " }" ; add-unique-rule : "A:active { color : " attr(alink) " }" ; add-unique-rule : "A:visited { color : " attr(vlink) " }" ; modify-context : ^[BGCOLOR]^[TEXT]^[LINK]^[ALINK]^[VLINK] ; add-unique-rule : ".centered { text-transform : center }" ; add-unique-rule : ".monospace { font-family : monospace }" ; add-unique-rule : ".italicized { font-style : italic }" }
Special thanks to
Chris Lilley (W3C), Håkon W. Lie (W3C), Jelks Cabaniss
CSS 1 and 2 authors and contributors
I also wish to thank the W3C HTML Working Group and CSS+FP Working Group chairmen and members who welcomed me in their Ali-Baba magic cave on behalf of Electricité de France. The permanent brainstorming and discussions happening there are the best seeds for imagination and creativity.