W3C: The CSS layout language

W3C Working Draft

This document: http://www.w3.org/pub/WWW/TR/WD-csslayout-951220.html
Latest version: http://www.w3.org/pub/WWW/TR/WD-csslayout.html

Bert Bos (bert@w3.org)

Håkon W Lie (howcome@w3.org)

Status of this document

This is a W3C Working Draft for review by W3C members and other interested parties. It is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to use W3C Working Drafts as reference material or to cite them as other than "work in progress". A list of current W3C tech reports can be found at: http://www.w3.org/pub/WWW/TR/

Note: since working drafts are subject to frequent change, you are advised to reference the above URL, rather than the URLs for working drafts themselves.







An HTML document is displayed on one or more pages. Each page has one or more frames. By default, there is only one page and only one frame, but by adding frames, you can do things like multi-column styles, etc. By adding pages, you can, e.g., make title pages and normal pages look different.

The document can be `flowed' into these frames as a whole, or it can be divided up into deveral flows, each filling up different frames. In this way you can for example put all quotations (`blockquote') into one frame and the rest of the text into another.

Defining pages

Here is an example of a page definition for a book-like layout:

	@layout titlepage emptypage
		(rightpage leftpage)* colophon;

This defines a sequence of pages, five different types in all. The `leftpage' and `rightpage' can be repeated as a pair. Each page is defined with a set of properties, one of which is a list of frames. Here is an example:

	@page titlepage {
		frames: titleframe toc;
		background: white;
		/* other properties */

After the @layout keyword, there is a list of names (arbitrarily chosen by the page designer). By default, each of these named pages will occur exactly once in the produced output (as separate sheets of paper, or as a stack of windows, for example). If there is an asterisk behind the name, it means that that page occurs only if there is text to put onto it. If there is more text than will fit, the page is repeated as needed. More explanation below, when the flows are defined.

Pages can be grouped within parentheses, like in the example above, which causes the group to repeat, instead of the individual pages.

Every page has one or more frames, which are the areas that will actually contain the text. The parts of the page that are not under any frame will be empty, except for the background (if any) of the page itself.

The following properties are allowed in a page definition:

The frames property holds a list of names for frames. The names are arbitrary and may be chosen freely by the designer. The order of the frames is significant, since by default text flows from frame to frame as each of them fills up.
background, bg-*
As elsewhere.

Note: gutter? mirror-page?

Defining frames

The following example shows how frames can be used to define a 2-column layout:

	@page some-page {		/* Define a page with 2 frames */
		frames: column1 column2
	@frame column1 {		/* Left half of page */
		geometry: 0% 0% 50% 100%;
		flow: main
	@frame column2 {		/* Right half of page */
		geometry: 51% 0% 50% 100%;
		flow: main

The most important properties of a frame are its size and position (`geometry') and the flow that it will contain. A flow is a part of the document, consisting of all those element that are marked with the same flow name. In the example above, the two frames receive the same flow, so that when column1 fills up, the rest of the flow goes into column2.

The following properties can be set on frames:

value: <length> <length> <length> <length> [<z-order>]

default: 0% 0% 100% 100% 0

The first four values are the x, y, width and height of the frame,relative to the upper left corner of the page on which the frame is defined. The values can be in absolute units (cm, in, etc.), screen-dependent units (px), or percentages of the page size.

The optional fifth value is the z-order, which is useful when frames overlap. The frame with the smallest z-order value is `in front', which means that it obscures the frame that it overlaps with.

Note: UA's may have a means of interactively changing z-order. An alpha-value (transparency) may be added later.

value: <name>

default: main

This marks the frame as a receiver of a certain flow (a subset of the HTML document). All frames with the same flow property form a chain, in the order that they are declared in the pages.

background, bg-*
As elsewhere.
As elsewhere.
value: <length> [<length> [<length> [<length>]]]

default: 0pt

How much space to leave between the border and the text. Note that the text may have its own margins and padding as well, which adds to the padding of the frame. If there is one value, it applies to all four sides. If there are two, the first is the pading at the top and the bottom, the second is the left and right. If there are three, then the third is for the bottom. If there are four, then the fourth is for the left side. The values are either absolute lengths (cm, in, etc.), screen-dependent lengths (px), relative lengths (%), or a mixture of these. The percentages are relative to the size of the frame.

By giving a frame a flow property that you know will never be used, such as flow: not-used, you can make sure that the frame stays empty, which is useful if you only want the frame to display its background image (the company logo, e.g.) and nothing else.

Note: other properties? (But see `defining styles for a flow' below.)

Note: in future, values can be expressions as well.

Note: running headers, page numbers etc. can may be be handled by special kinds of frames?

Defining flows & targets

All frames are marked with a flow and so are all the elements (or rather, the blocks and inline objects that are created for each element). The following example puts all blockquotes and selected paragraphs onto the flow called `sidebar':

	BLOCKQUOTE, P.note { flow: sidebar }

Note: Duplicating an element onto multiple flows could cater for automatic tables of content (at least, if the toc has its own frame). There has to be an implicit link between the two flows, but if that can be defined, its a simple matter of saying:

	H1, H2 {flow: main toc}

to have the H1 and H2 appear in both the main flow and the toc flow. Unfortunately, this fails if the suggestion for sequential flows below is implemented.

Not only the elements themselves have a flow, but if the element is a hyperlink to some other document, than that other document can be given a flow, too. But in this case, the property is called `target'. For example, to ensure that the document pointed to by an A element ends up in the `foo' flow, (when the user clicks on it) one would do this:

	A { target: foo }

When the link is activated, the document pointed to by the A element will replace the current flow called `foo'. (The UA will normally have a Back-button that restored the previous situation.) The new document may have its own style sheet, but only the style part of it will be applied, not the layout, since there is already a layout in use on the screen.

However, if the target is `none' (the default), the new document will completely replace the current one, including its layout.

Anchors aren't the only elements that specify hyperlinks, LINK and IMG are other examples. The difference between A and IMG is that, normally, the target of the A is not displayed until after the user activates it, while the IMG is displayed immediately. Here are some examples:

	LINK.toc { target: toc }	/* Put ToC in the toc flow */
	IMG.logo { flow: logo-flow }	/* Add image to the logo-flow */

Note: in the future, we want to have explicit control over when an element gets replaced and by what it is replaced. A suggested syntax is: IMG {replace: [SRC]}, which tells the formatter to replace the IMG element by the document pointed to by the SRC attribute. If you do A.detail {replace: [HREF]; target: sidebar}, you'll have an auto-expanding hyperlink.

Definition of the flow and target properties:

value: <name>

initial: main

The flow property determines in which flow the element will be displayed. There should be a chain of frames with the same flow property, otherwise the element will not be displayed anywhere. The property is inherited.

value: none | <name-of-flow>

initial: none

The value `none' means that the document pointed to by this element will completely replace the current document and its layout. If the value is not none, the referenced document will replace the flow of that name in the current document and any layout info in the new document will be ignored. (Its style will be applied, though.)

The property is inherited (but, of course, it is only effective if the element is the source of a hyperlink).

Note: how about adding a keyword `from-predecessor' (or something) to the possible values for `flow'? This will enable a document to be split into flows `lengthwise' as it were. E.g., a header and all paragraphs following it can form a single flow, even though you have no way of addressing these paragraphs with the current syntax for selectors.

	H1 {flow: head}
	H2 {flow: body}
	H2.appendix {flow: appendix}
	P {flow: from-predecessor}

The document `H1 P P H2 P P H2 P P H2.appendix P P' will be split as follows: [H1 P P] [H2 P P H2 P P] [H2.appendix P P]'. The `from-predecessor' keyword would be the default value for `flow'.

Note: how about adding another keyword `next-flow', which would assign the next defined flow name to the property. Example:

	H1 { flow: next-flow }

will split the document into as many flows as there are H1's. The H1 and all elements up to the next H1 will form a single flow. What if there are more H1's than flows? Are the last elements forced into a single flow? Flows aren't explicitly ordered in the current syntax, but they are implicitly ordered, because the frames are ordered and you order the flows by the frames on which they appear first. Of course, `target' can have this same value.

When to repeat a page and when to add a scrollbar

Both the frames and the document are now divided into flows. Each flow from the document is put into the chain of frames with the same flow name. When the first frame fills up, the rest of the document is put into the next frame, etc.

When the frame that is next in line is part of a repeating page, that page is created (if it's part of a repeating group, then together with all other pages in the group.) If the flow fills up the frames on a repeating page, more (groups of) pages are added as needed, until the end of the flow is reached.

On the other hand, when the last frame of a flow fills up and it is not part of a repeating page, then the rest of the flow cannot be displayed. Instead, there must be some sort of an indication on the page that there is more text that couldn't be shown. One way is to add a scrollbar next to the last frame, or a button saying `more...'. It is currently undefined what the UA should do, but at least there must be some way for the user to display the rest of the flow.

It is recommended that the UA put some sort of (localized) `continued on next page' sign at the bottom of every frame except the last one.

Defining styles for a flow

Sometimes it is desirable to give the text in a certain frame a distinctive style, which is not so much a function of its structure, as of the fact that it is displayed in that frame. For example, one may want to give the first frame a larger font than the other frames. The way to do this is to set styles for each element that are conditionalized on that element appearing in a certain frame:

	P:top-frame {font-weight: 1}
	P:sidebar {font-size: 8pt}

This says that the part of a paragraph that is inside a frame called `sidebar' must be displayed with an 8pt font, and the part that is inside a frame called `top-frame' must be in bold.

Note: this is ambiguous, since one cannot distinguish the special pseudo-classes `link', `visited', `first-line' and `first-letter' from the frame names. Can this be solved elegantly? Should it be solved?


Here is the equivalent of the default layout:

	@layout the-page;
	@page the-page { frames: the-frame }
	@frame the-frame {
		geometry: 0% 0% 100% 100%;
		flow: main;

Here is a 2-column magazine with a wide initial frame for the title on the first page. There is no need to declare flows, since all frames are part of the same (default) flow.

	@layout first-page other-page*
	@page first-page {
		frames: title-frame first-col1 first-col2
	@page other-page {
		frames: col1 col2
	@frame title-frame {
		geometry: 0pt 0pt 100% 20%
	@frame first-col1 {
		geometry: 0pt 20% 50% 80%;
		frame-width: 0pt thin 0pt 0pt
	@frame first-col2 {
		geometry: 50% 20% 50% 80%;
	@frame col1 {
		geometry: 0% 0% 50% 100%;
		frame-width: 0pt thin 0pt 0pt
	@frame col2 {
		geometry: 50% 0% 50% 100%

The columns also have a thin line between them.

W3C: The World Wide Web Consortium: http://www.w3.org/pub/WWW/Consortium/