CSS2 notes

Topics:


Layout with nested boxes

Instead of a flat canvas with mutually unrelated frames, whose size only depends on the window size, one could also use a nested boxes paradigm. The nested boxes don't have an formula for their size, but instead they have a layout policy (from among a few predefined ones) with which they position the boxes nested inside them.

This model is used in many windowing toolkits, like Motif, Athena, Awt and Tk. Sometimes a distinction is made between so-called `manager widgets', which accept children, and `core widgets', which don't.

Here's an attempt at defining such a model for CSS. It should be compared with the earlier proposal that used only a flat frame space. Maybe this one is easier, or more flexible, or maybe not...

Margin, border, padding, background

The margin around a frame is the minimum distance between this frame and any other. Thus, two frames are always separated by the maximum of their margins. If the frame is put against the edge of its parent, the margin is the distance between this frame and the padding. The margin is not part of the frame, it just specifies how much this frame `repels' any other. The margin has no color, (or, in other words: it is transparent).

The frame border has a style, a width and a color. The values are the same as for the corresponding properties in CSS1 (where thay apply to HTML elements).

The padding is the distance between the border and anything that is contained within the frame, be it text or other frames. This padding effectively reduces the inside area of the frame. It doesn't disappear if it is next to something with a larger padding or margin (unlike the margin).

The background fills the inside of the frame, that is, the padding and any part of the inside not covered by text or other frames. The property has the same values as the one described in CSS1.

Frame size

A frame has an initial size, which may be related to the initial size of its parent, or be independent of that. Frames can either have a fixed size, or be resizable. In the latter case, they can be resized either because their contents requested them to resize, or because their parents resized them.

The resize policy is encoded in a property, that can have values `fixed' (keep initial size always), `resizable' (parent can resize the frame), `shrink-to-fit' (frame always adapts to exactly enclose the children), `rubber' (frame is always large enough to enclose its children, though the parent may make it larger than that), or `free' (frame will try to fit its children, but parent can resize it to any size.)

Position

The position of a frame is completely controlled by the layout policy of its parent. A frame has an initial position, which serves as a hint to the parent. Whether that hint has any effect depends on the parent's policy.

Possible policies for layout out children include the following:

rows
All children are layed out like words on a line, wrapping to the next row if a row becomes filled. All children are treated as if their margins were as large as the largest margin among them (computed separately for top, right, bottom and left margins). Variations are (* = default): left-to-right*/right-to-left/justified/centered, align-bottom/align-top*/align-middle, force-same-height, force-same-width/extend-last/keep-width*, top-to-bottom*/bottom-to-top. (`extend-last' means that the last child on the row is made wider, to fill the remaining space. If that child is not resizable, the last but one is tried, etc.) If the resize policy is `shrink-to-fit', `rubber' or `free', the frame will put all its children in a single row.
columns
Like rows, but children fill columns rather than rows. Analogous variations.
grid
The frame has an invisible grid, with cells that are as large as the largest of its children. Each child is put in a separate grid cell. Variations: left-to-right-top-to-bottom*/ right-to-left-top-to-bottom/ left-to-right-bottom-to-top/ right-to-left-bottom-to-top/ close-to-position-hints, align-top/align-middle*/align-bottom, align-left/align-center*/align-right.
cascade
The children overlap, in a cascade from top left to bottom-right. Variations: force-same-width, force-same-height.
tabbed
Only one child is visible at a time, but there is a row of tabs, each corresponding to a child (and showing the name of the child frame). Clicking on a tab brings that frame to the top. All children are forced to the same width and height.
bulletin board
All position hints are honored. No positions or sizes are forced.
icon box
All children are represented as thumbnails (icons). Clicking on one of them shows the frame, aligned in the upper left corner (and closes any other frame). Variations: align-left/align-center/align-right, align-top/align-middle/align-bottom, force-same-width, force-same-height.
random
Children are placed at a random location. Variations: uniform-distribution/normal-distribution, force-same-width, force-same-height.
spiral
Child frames are placed along a square spiral. Variations: inwards/outwards, left-turning/right-turning, force-same-width, force-same-height.
pack
Children are packed as tightly as possible, rearranging them to take up as small a surface as possible.
tiles
Children are placed close to their hinted position, but shifted so as not to overlap already existing frames. When one frame changes size, all later frames may have to shift position as well.

Z-order and transparency

In a layout where frames may overlap, there is an ordering among sibling frames, given by the z-order, that specifies which frames are `on top' of which other frames. The z-order is a number, giving the `distance' from the user. Frames with higher z-orders can be obscured by frames with lower z-orders.

Frames can have either a fixed z-order, or an interactively changeable one. There is a way for the user to cycle all changeable sibling frames up or down, meaning that each frame gets the z-order of the next lower (resp. higher) changeable frame.

Frames can be partially transparent, causing frames behind it to be visible through it. There are two kinds of transparency: the background of the frame can be transparent (instead of a color or pattern), and the frame as a whole (including everything in it) can have a transparency factor (alpha-channel).

An alpha of 100% makes a frame completely transparent, 0% makes the frame opaque. Note that there are several layers of frames, first the siblings of a frame, then the background of the parent, then all the other frames behind the parent.

Frames can also specify the value `wedge', which means that it pushes aside any text that might otherwise be hidden under it. In other word, it causes text in lower frames to `wrap around' this frame.

Manager frames, leaf frames and flows

A frame either contains other frames, or it contains text (or it contains nothing at all, which is a special case of text). If it contains other frames, it is called a manager frame and it applies its layout policy to its children. If it contains text, it is called a leaf frame and its layout policy is not used.

`Text' actually means anything coming from the content of the HTML page; it may be text, an image, or anything else that is expressed in the HTML.

Every frame has a flow. Also, every HTML element is assigned a flow in the style sheet, causing that element to be displayed in a frame with the same flow. Frames can either form a flow on their own, or they can share a flow. In the latter case, when one frame fills up, the text continues in the next frame with the same flow.

It is a compile-time error if a frame has both children and a shared flow. It is a run-time error if a frame has children, and some instance of an HTML element has a flow equal to this frame's name.

Resizing of leaf frames

Leaf frames normally impose their size on their content, but some types of content may cause the frame to try to resize itself (if the resize policy is `shrink-to-fit', `rubber' or `free'). This happens if part of the content has a width that is wider than the frame (or more precisely: if max(0, margin-left) + border-width-left + padding-left + max(0, width) + padding-right + border-width-right + max(0, margin-right) is larger than the frame width, minus its padding).

The height of leaf frames can change if the resize policy is `shrink-to-fit', `rubber' or `free' and then only if the frame is the last frame of a flow. In that case, the frame will try to grow vertically to hold all of its content.

CSS syntax

The window itself is the top level frame. It is called `main'. Other frames can have any name, but the names must be unique among them. A frame definitions looks like this:

  @frame NAME {
    PROPERTY: VALUE;
    PROPERTY: VALUE;
    ...
  }
    

Each frame has the following properties. Frames don't inherit properties from each other.

parent

Gives the name of the parent frame. Obligatory for all frames, except for the main frame.

width & height

value: <expression>
initial: 100% 100%

The expressions can contain some sum of absolute lengths (px, cm, in, mm, pt) and percentages. The percentages refer to the initial size of the parent. Whether this size is used at all depends on the parent's layout policy and the frame's own resize policy. If the parent resizes, this size is not recomputed. Instead, the various layout policies and resize policies determine the new size of the frame.

x & y

value: <expression>
initial: 0% 0%

The position hint of the frame. The parent's layout policy determines if the hint is used or not. Meaning is as for size.

margin

Syntax is the same as in CSS1, meaning is explained above.

border-style, border-width, border-color

The same as in CSS1.

padding

Syntax as in CSS1, meaning is explained above.

background

As in CSS1.

resize-policy

value: fixed | resizable | shrink-to-fit | rubber | free
initial: resizable

As explained above.

layout-policy

value: <policy> <variation>*
initial: columns

Z-order

value: <number> [fixed|variable]
initial: 0 fixed

Any number can be used for the z-order, only their relative values among sibling frames is important. The lowest number is closest to the viewer.

If `variable' is added, it means that the user can interactively change the z-order. The way to do that is dependent on the common practice for window handling on the platform. It may be by cycling all sibling frames up or down, or by exchanging two frames, or some other way.

If `fixed' is added, or if neither `fixed' nor `variable' is specified, the z-order cannot be changed interactively. This is most useful in parents where all frames have fixed z-orders, or for a frame that should stay on top off all its siblings, or behind them.

alpha

value: <number> | <percentage> | wedge
initial: 0

Transparency, as explained above.

flow

value: <ident> initial: (name of frame)

As explained above.

If not specified, a frame will have a flow with the same name as the frame itself.

The frames that share a flow are ordered implicitly, by the order in which they appear in the style sheet.

Examples

A layout with a header, a toc along the left side, an index along the right and the main text between them.

  @frame main {
    background: black;
    layout-policy: columns force-same-width extend-last
  }
  @frame header {
    parent: main;
    size: 100% 2cm
  }
  @frame body {
    parent: main;
    layout-policy: row force-same-height extend-last;
    resize-policy: rubber
  }
  @frame toc {
    parent: body;
    resize-policy: rubber;
  }
  @frame text {
    parent: body;
    resize-policy; free;
  }
  @frame index {
    parent: body;
    resize-policy: rubber
  }
    

Notes

Many things can be named differently, grouped together differently, or changed to a different model altogether. The resize and layout policies could be specified for each of the four sides of a frame separately. The interplay of initial size and behavior when resized is often difficult to understand.

On the whole, my impression is that this kind of layout is better suited to user interfaces than to text. It is most useful when there are many small objects, like buttons and icons. The large text areas of a typical document don't move around as much and it is difficult to imagine them changing in any other way than by simple scaling.

But on the other hand, much as I dilike it, HTML might be heading towards being a user-interface language, instead of a text structuring language...


Bert Bos