Chapter 1. Basic Concepts

Adding Styles to HTML and XHTML

Styles can be applied to documents in three distinct ways, as discussed in the following sections.

Inline Styles

In HTML and XHTML, style information can be specified for an individual element via the style attribute. The value of a style attribute is a declaration block (see the section “Rule Structure”) without the curly braces:

<p style="color: red; background: yellow;">Look out!
This text is alarmingly presented!</p>

Note that, as of this writing, a full style sheet cannot be placed into a style attribute. Only the content of a single declaration block can be used as a style attribute value. For example, it is not possible to place hover styles (using :hover) in a style attribute, nor can one use @import in this context.

Although typical XML document languages (e.g., XHTML 1.0, XHTML 1.1, and SVG) support the style attribute, it is unlikely that all XML languages will support a similar capability. Because of this and because it encourages poor authoring practices, authors are generally discouraged from using the style attribute.

Embedded Style Sheets

A style sheet can be embedded at the top of an HTML or XHTML document using the style element, which must appear within the head element:

<html><head><title>Stylin'!</title>
<style type="text/css">
h1 {color: purple;}
p {font-size: smaller; color: gray;}
</style>
</head>
   ...
</html>

XML languages may or may not provide an equivalent capability; always check the language DTD to be certain.

External Style Sheets

Styles can be listed in a separate file. The primary advantage to a separate file is that by collecting commonly used styles in a single file, all pages using that style sheet can be updated by editing a single style sheet. Another key advantage is that external style sheets are cached, which can help reduce bandwidth usage. An external style sheet can be referenced in one of the following three ways:

@import directive

One or more @import directives can be placed at the beginning of any style sheet. For HTML and XHTML documents, this would be done within an embedded style sheet:

<head><title>My Document</title>
<style type="text/css">
@import url(site.css);
@import url(navbar.css);
@import url(footer.css);
body {background: yellow;}
</style>
</head>

Note that @import directives can appear at the top (and, according to the specification, only at the top) of any style sheet. Thus, one style sheet could import another, which in turn would import a third.

xml-stylesheet processing instruction

In XML documents (such as XHTML documents sent with a mime-type of “text/xml,” “application/xml,” or “application/xhtml+xml”), an xml-stylesheet processing instruction can be used to associate a style sheet with a document. Any xml-stylesheet processing instructions must be placed in the prolog of an XML document. Multiple xml-stylesheet processing instructions are permitted. The media pseudo-attribute can be used to restrict a style sheet to one or more forms of media:

<?xml-stylesheet type="text/css" href="basic.css"
  media="all"?>
<?xml-stylesheet type="text/css" href="web.css"
  media="screen"?>
<?xml-stylesheet type="text/css" href="paper.css"
  media="print"?>

It is also possible to link to alternate style sheets with the xml-stylesheet processing instruction:

<?xml-stylesheet type="text/css" href="basic.css"?>
<?xml-stylesheet alternate="yes" title="Classic"
  type="text/css" href="oldschool.css"?>
<?xml-stylesheet alternate="yes" title="Futuristic"
  type="text/css" href="3000ad.css"?>

Rule Structure

A style sheet consists of one or more rules that describe how page elements should be presented. Every rule has two fundamental parts: the selector and the declaration block. Figure 1-1 illustrates the structure of a rule.

Figure 1-1. Rule structure

On the left side of the rule, we find the selector, which selects the parts of the document to which the rule should be applied. Selectors can stand singly or be grouped as a comma-separated list; e.g., to select the top three heading levels at once, the selector group would be h1, h2, h3. On the right side of the rule, we have the declaration block. A declaration block is made up of one or more declarations; each declaration is a combination of a CSS property and a value of that property.

The declaration block is always enclosed in curly braces. A declaration block can contain several declarations; each declaration must be terminated with a semicolon (;). The exception is the final declaration in a declaration block, for which the semicolon is optional.

Each property, which represents a particular stylistic parameter, is separated from its value by a colon (:). Property names in CSS are not case-sensitive. Legal values for a property are defined by the property description. Chapter 4 provides details on acceptable values for CSS properties.

Comments

Including comments in CSS is simple. You open with /* and end with */, like this:

/* This is a comment! */

Comments can be multiple lines long:

/* This is a comment!
  This is a continuation of the comment.
 And so is this. */

They can also occur anywhere within a style sheet except in the middle of a token (property name or value):

h1/* heading-level-1 */ {color /* foreground color */: 
   rgba(23,58,89,0.42) /* RGB + opacity */;}

HTML (properly SGML) comments (<!-- such as this -->) are permitted in style sheets so as to hide the styles from browsers so old that they don’t understand HTML 3.2. They do not act as CSS comments; that is, anything contained in an HTML comment will be seen and interpreted by the CSS parser.

Style Precedence

A single HTML or XHTML document can import and link to multiple external style sheets, contain one or more embedded style sheets, and make use of inline styles. In the process, it is quite possible that some rules will conflict with one another. CSS uses a mechanism called the cascade to resolve any such conflicts and arrive at a final set of styles to be applied to the document. Two key components of the cascade are specificity and inheritance.

Specificity Calculations

Specificity describes the weight of a selector and any declarations associated with it. The following table summarizes the components of specificity summation.

Selector typeExampleSpecificity

Universal selector

Combinator

*

+

0,0,0,0

Element identifier

Pseudo-element identifier

div

::first-line

0,0,0,1

Class identifier

Pseudo-class identifier

Attribute identifier

.warning

:hover

[type="checkbox"]

0,0,1,0

ID identifier

#content

0,1,0,0

Inline style attribute

style="color: red;"

1,0,0,0

Specificity values are cumulative; thus, a selector containing two element identifiers and a class identifier (e.g., div.aside p) has a specificity of 0,0,1,2. Specificity values are sorted in right-to-left precedence; thus, a selector containing 11 element identifiers (0,0,0,11) has a lower specificity than a selector containing just a single class identifier (0,0,1,0).

The !important directive gives a declaration more weight than nonimportant declarations. The declaration retains the specificity of its selectors and is used only in comparison with other important declarations.

Inheritance

The elements in a document form a treelike hierarchy with the root element at the top and the rest of the document structure spreading out below it (which makes it look more like a tree root system, really). In an HTML document, the html element is at the top of the tree, with the head and body elements descending from it. The rest of the document structure descends from those elements. In such a structure, elements lower down in the tree are descendants of the ancestors, which are higher in the tree.

CSS uses the document tree for the mechanism of inheritance, in which a style applied to an element is inherited by its descendants. For example, if the body element is set to have a color of red, that value propagates down the document tree to the elements that descend from the body element. Inheritance is interrupted only by a style rule that applies directly to an element. Inherited values have no specificity at all (which is not the same as having zero specificity).

Note that some properties are not inherited. A property will always define whether it is inherited. Some examples of noninherited properties are padding, border, margin, and background.

The Cascade

The cascade is how CSS resolves conflicts between styles; in other words, it is the mechanism by which a user agent decides, for example, what color to make an element when two different rules apply to it and each one tries to set a different color. The following steps constitute the cascade:

  1. Find all declarations that contain a selector that matches a given element.

  2. Sort by explicit weight all declarations applying to the element. Those rules marked !important are given greater weight than those that are not. Also, sort by origin all declarations applying to a given element. There are three origins: author, reader, and user agent. Under normal circumstances, the author’s styles win out over the reader’s styles. !important reader styles are stronger than any other styles, including !important author styles. Both author and reader styles override the user agent’s default styles.

  3. Sort by specificity all declarations applying to a given element. Those elements with a higher specificity have more weight than those with lower specificity.

  4. Sort by order all declarations applying to a given element. The later a declaration appears in a style sheet or a document, the more weight it is given. Declarations that appear in an imported style sheet are considered to come before all declarations within the style sheet that imports them, and have a lower weight than those in the importing style sheet.

Any presentational hints that come from non-CSS sources (e.g., the preference dialog within a browser) are given the same weight as the user agent’s default styles (see step 2 above).

Element Classification

Broadly speaking, CSS groups elements into two types: nonreplaced and replaced. Although the types may seem rather abstract, there actually are some profound differences in how the two types of elements are presented. These differences are explored in detail in Chapter 7 of CSS: The Definitive Guide, third edition (O’Reilly).

Nonreplaced Elements

The majority of HTML and XHTML elements are nonreplaced elements, which means their content is presented by the user agent inside a box generated by the element itself. For example, <span>hi there</span> is a nonreplaced element, and the text hi there will be displayed by the user agent. Paragraphs, headings, table cells, lists, and almost everything else in HTML and XHTML are nonreplaced elements.

Replaced Elements

In contrast, replaced elements are those whose content is replaced by something not directly represented by document content. The most familiar HTML example is the img element, which is replaced by an image file external to the document itself. In fact, img itself has no actual content, as we can see by considering a simple example:

<img src="howdy.gif" alt="Hi">

There is no content contained in the element—only an element name and attributes. Only by replacing the element’s lack of content with content found through other means (in this case, loading an external image specified by the src attribute) can the element have any presentation at all. Another example is the input element, which may be replaced with a radio button, checkbox, or text input box, depending on its type. Replaced elements also generate boxes in their display.

Element Display Roles

In addition to being replaced or not, there are two basic types of element display roles in CSS3: block-level and inline-level. All CSS3 display values fall into one of these two categories. It can be important to know which general role a box falls into, since some properties only apply to one type or the other.

Block-Level

Block-level boxes are those where the element box (by default) fills its parent element’s content area width and cannot have other elements to its sides. In other words, block-level elements generate “breaks” before and after the element box. The most familiar block elements from HTML are p and div. Replaced elements can be block-level elements but usually are not.

List items are a special case of block-level elements. In addition to behaving in a manner consistent with other block elements, they generate a marker—typically a bullet for unordered lists or a number for ordered lists—which is “attached” to the element box. Except for the presence of this marker, list items are identical to other block elements.

The display values that create block boxes are: block, list-item, table, table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, table-caption, flex, and grid

Inline-Level

Inline-level boxes are those where an element box is generated within a line of text and does not break up the flow of that line. Perhaps the best-known inline element is the a element in HTML and XHTML. Other examples are span and em. These elements do not generate a break before or after themselves, so they can appear within the content of another element without disrupting its display.

Note that although the CSS block and inline elements have a great deal in common with HTML and XHTML block- and inline-level elements, there is an important difference. In HTML and XHTML, block-level elements cannot descend from inline-level elements, whereas in CSS, there is no restriction on how display roles can be nested within each other.

The display values that create inline boxes are: inline, inline-block, inline-table, and ruby. As of this writing, it was not explicitly defined that the various Ruby-related values (e.g., ruby-text) also generate inline boxes, but this seems the most likely outcome.

Run-In

A special case is run-in boxes, defined by display: run-in, which can generate either a block or an inline box depending on the situation. The rules that decide the outcome are:

  1. If the run-in itself contains a block box, the run-in generates a block box.

  2. If that’s not the case, and the run-in is immediately followed by a sibling block box that is neither floated nor absolutely positioned, the run-in box becomes the first inline box of the sibling block box.

  3. If neither condition applies, the run-in generates a block box.

In the case where a run-in is inserted as the first inline of its sibling block box (rule 2 above), it does not inherit property values from that block box. Instead, it continues to inherit from its structural parent element. Thus, if the sibling block box has color: green applied to it, the green will not be inherited by the run-in element even though it is visually a part of the block box.

Basic Visual Layout

CSS defines algorithms for laying out any element in a document. These algorithms form the underpinnings of visual presentation in CSS. There are two primary kinds of layout, each with very different behaviors: block-level and inline-level layout.

Block-Level Layout

A block-level box in CSS generates a rectangular box called the element box, which describes the amount of space occupied by an element. Figure 1-2 shows the various components of an element box.

Figure 1-2. Box model details

The following rules apply to an element box:

  • The background of the element box extends to the outer edge of the border, thus filling the content, padding, and border areas. If the border has any transparent portions (e.g., it is dotted or dashed), the background will be visible in those portions. The background does not extend into the margin areas of the box. Any outlines are drawn in the margin area and do not affect layout.

  • Only the margins, height, and width of an element box may be set to auto.

  • Only margins can be given negative values.

  • The padding and border widths of the element box default to 0 (zero) and none, respectively.

  • If box-sizing is content-box (the default value), the property width defines only the width of the content area; any padding, borders, or margins are added to it. The same is true for height with respect to the height.

  • If box-sizing is border-box, the property width defines the total width of the content, padding, and borders; any margins are added to it. The same is true for height with respect to the height.

Inline Layout

An inline-level box in CSS generates one or more rectangular boxes called inline boxes, depending on whether the inline box is broken across multiple lines. The following rules apply to inline box:

  • For the properties left, right, top, bottom, margin-left, margin-right, margin-top, and margin-bottom, any value of auto is converted to 0 (zero).

  • width and height do not apply to nonreplaced inline boxes.

  • For replaced inline boxes, the following rules apply:

    • If height and width are both auto and the element has an intrinsic width (e.g., an image), that value of width is equal to the element’s intrinsic width. The same holds true for height.

    • If height and width are both auto and the element does not have an intrinsic width but does have an intrinsic height and layout ratio, then width is set to be the intrinsic height times the ratio.

    • If height and width are both auto and the element does not have an intrinsic height but does have an intrinsic width and layout ratio, then height is set to be the intrinsic width divided by the ratio.

There are a few rules even more obscure than those last two, which are too lengthy to include here; see http://w3.org/TR/css3-box/#inline-replaced for details.

All inline elements have a line-height, which has a great deal to do with how the elements are displayed. The height of a line of text is determined by taking into account the following factors:

Anonymous text

Any string of characters not contained within an inline element. Thus, in the markup:

<p> I'm <em>so</em> happy!</p>

…the sequences “I’m ” and “ happy!” are anonymous text. Note that the spaces are part of the anonymous text, as a space is a character like any other.

Em-box

The em-box defined in the given font; otherwise known as the character box. Actual glyphs can be taller or shorter than their em-boxes, as discussed in Chapter 5 of CSS: The Definitive Guide, third edition (O’Reilly). In CSS, the value of font-size determines the height of each em-box.

Content area

In nonreplaced elements, the content area can be the box described by the em-boxes of every character in the element, strung together, or else the box described by the character glyphs in the element. The CSS2.1 specification allows user agents to choose either. This text uses the em-box definition for simplicity’s sake. In replaced elements, the content area is the intrinsic height of the element plus any margins, borders, or padding.

Leading

The leading is the difference between the values of font-size and line-height. Half this difference is applied to the top and half to the bottom of the content area. These additions to the content area are called, not surprisingly, half-leading. Leading is applied only to nonreplaced elements.

Inline box

The box described by the addition of the leading to the content area. For nonreplaced elements, the height of the inline box of an element will be equal to the value for line-height. For replaced elements, the height of the inline box of an element will be equal to the content area, as leading is not applied to replaced elements.

Line box

The shortest box that bounds the highest and lowest points of the inline boxes that are found in the line. In other words, the top edge of the line box will be placed along the top of the highest inline box top, and the bottom of the line box is placed along the bottom of the lowest inline box bottom. (See Figure 1-3.)

Figure 1-3. Inline layout details

Floating

Floating allows an element to be placed to the left or right of its containing block (which is the nearest block-level ancestor element), with following content flowing around the element. Any floated element automatically generates a block box, regardless of what type of box it would generate if not floated. A floated element is placed according to the following rules:

  • The left (or right) outer edge of a floated element may not be to the left (or right) of the inner edge of its containing block.

  • The left (or right) outer edge of a floated element must be to the right (or left) of the right (left) outer edge of a left-floating (or right-floating) element that occurs earlier in the document’s source, unless the top of the later element is below the bottom of the former.

  • The right outer edge of a left-floating element may not be to the right of the left outer edge of any right-floating element to its right. The left outer edge of a right-floating element may not be to the left of the right outer edge of any left-floating element to its left.

  • A floating element’s top may not be higher than the inner top of its containing block.

  • A floating element’s top may not be higher than the top of any earlier floating or block-level element.

  • A floating element’s top may not be higher than the top of any line box with content that precedes the floating element.

  • A left (or right) floating element that has another floating element to its left (right) may not have its right (left) outer edge to the right (left) of its containing block’s right (left) edge.

  • A floating element must be placed as high as possible.

  • A left-floating element must be put as far to the left as possible, and a right-floating element as far to the right as possible. A higher position is preferred to one that is farther to the right or left.

Positioning

When elements are positioned, a number of special rules come into play. These rules govern not only the containing block of the element, but also how it is laid out within that element.

Types of Positioning

Static positioning

The element’s box is generated as normal. Block-level elements generate a rectangular box that is part of the document’s flow, and inline-level boxes generate one or more line boxes that flow within their parent element.

Relative positioning

The element’s box is offset by some distance. Its containing block can be considered to be the area that the element would occupy if it were not positioned. The element retains the shape it would have had were it not positioned, and the space that the element would ordinarily have occupied is preserved.

Absolute positioning

The element’s box is completely removed from the flow of the document and positioned with respect to its containing block, which may be another element in the document or the initial containing block (described in the next section). Whatever space the element might have occupied in the normal document flow is closed up, as though the element did not exist. The positioned element generates a block box, regardless of the type of box it would generate if it were in the normal flow.

Fixed positioning

The element’s box behaves as though set to absolute, but its containing block is the viewport itself.

The Containing Block

The containing block of a positioned element is determined as follows:

  1. The containing block of the root element (also called the initial containing block) is established by the user agent. In HTML, the root element is the html element, although some browsers may use body.

  2. For nonroot elements, if an element’s position value is relative or static, its containing block is formed by the content edge of the nearest block-level, table-, cell-, or inline-block ancestor box. Despite this rule, relatively positioned elements are still simply offset, not positioned with respect to the containing block described here, and statically positioned elements do not move from their place in the normal flow.

  3. For nonroot elements that have a position value of absolute, the containing block is set to the nearest ancestor (of any kind) that has a position value other than static. This happens as follows:

    1. If the ancestor is block-level, the containing block is that element’s outer padding edge; in other words, it is the area bounded by the element’s border.

    2. If the ancestor is inline-level, the containing block is set to the content edge of the ancestor. In left-to-right languages, the top and left of the containing block are the top and left content edges of the first box in the ancestor, and the bottom and right edges are the bottom and right content edges of the last box. In right-to-left languages, the right edge of the containing block corresponds to the right content edge of the first box, and the left is taken from the last box. The top and bottom are the same.

    3. If there are no ancestors as described in 3a and 3b, the absolutely positioned element’s containing block is defined to be the initial containing block.

Layout of Absolutely Positioned Elements

In the following sections, these terms are used:

Shrink-to-fit

Similar to calculating the width of a table cell using the automatic table layout algorithm. In general, the user agent attempts to find the minimum element width that will contain the content and wrap to multiple lines only if wrapping cannot be avoided.

Static position

The place where an element’s edge would have been placed if its position were static.

Horizontal layout of absolutely positioned elements

The equation that governs the layout of these elements is:

left + margin-left + border-left-width + padding-left + 
width + padding-right + border-right-width + 
margin-right + right + vertical scrollbar width (if any) = 
width of containing block

The width of any vertical scrollbar is determined by the user agent and cannot be affected with CSS.

For nonreplaced elements, the steps used to determine horizontal layout are:

  1. If all of left, width, and right are auto, first reset any auto values for margin-left and margin-right to 0. Then, if direction is ltr, set left to the static position and apply the rule given in step 3c. Otherwise, set right to the static position and apply the rule given in step 3a.

  2. If none of left, width, and right is auto, pick the rule that applies from the following list:

    1. If both margin-left and margin-right are set to auto, solve the equation under the additional constraint that the two margins get equal values.

    2. If only one of margin-left or margin-right is set to auto, solve the equation for that value.

    3. If the values are overconstrained (none is set to auto), ignore the value for left if direction is rtl (ignore right if direction is ltr) and solve for that value.

  3. If some of left, width, and right are auto, but others are not, reset any auto values for margin-left and margin-right to 0. From the following list, pick the one rule that applies:

    1. If left and width are auto and right is not, the width is shrink-to-fit. Solve the equation for left.

    2. If left and right are auto and width is not, then if direction is ltr, set left to the static position (otherwise, set right to the static position). Solve the equation for left (if direction is rtl) or right (if direction is ltr).

    3. If width and right are auto and left is not, the width is shrink-to-fit. Solve the equation for right.

    4. If left is auto and width and right are not, solve the equation for left.

    5. If width is auto and left and right are not, solve the equation for width.

    6. If right is auto and left and width are not, solve the equation for right.

For replaced elements, the steps used to determine horizontal layout are:

  1. Determine the value of width as described for inline replaced elements (see “Inline Layout”).

  2. If both left and right are set to auto, then if direction is ltr, set left to the static left position. If direction is rtl, set right to the static right position.

  3. If either or both of left and right are set to auto, reset any auto values for margin-left and margin-right to 0.

  4. If neither left nor right is set to auto and both margin-left and margin-right are set to auto, solve the equation under the additional constraint that the two margins get equal values.

  5. If the values are overconstrained (none is set to auto), ignore the value for left if direction is rtl (ignore right if direction is ltr) and solve for that value.

Vertical layout of absolutely positioned elements

The equation that governs the layout of these elements is:

top + margin-top + border-top-width + padding-top + height 
+ padding-bottom + border-bottom-width + margin-bottom + 
bottom + horizontal scrollbar height (if any) = 
height of containing block

The height of any horizontal scrollbar is determined by the user agent and cannot be affected with CSS.

For nonreplaced elements, the steps used to determine vertical layout are:

  1. If all of top, height, and bottom are auto, set top to the static position and apply the rule given in step 3c.

  2. If none of top, height, and bottom is auto, pick the one rule that applies from the following list:

    1. If both margin-top and margin-bottom are set to auto, solve the equation under the additional constraint that the two margins get equal values.

    2. If only one of margin-top or margin-bottom is set to auto, solve the equation for that value.

    3. If the values are overconstrained (none is set to auto), ignore the value for bottom and solve for that value.

  3. If some of top, height, and bottom are auto, but others are not, pick the one rule that applies from the following list:

    1. If top and height are auto and bottom is not, the height is based on the element’s content (as it would be in the static flow). Reset any auto values for margin-top and margin-bottom to 0 and solve the equation for top.

    2. If top and bottom are auto and height is not, set top to the static position. Reset any auto values for margin-top and margin-bottom to 0 and solve the equation for bottom.

    3. If height and bottom are auto and top is not, the height is based on the element’s content (as it would be in the static flow). Reset any auto values for margin-top and margin-bottom to 0 and solve the equation for bottom.

    4. If top is auto and height and bottom are not, reset any auto values for margin-top and margin-bottom to 0 and solve the equation for top.

    5. If height is auto and top and bottom are not, reset any auto values for margin-top and margin-bottom to 0 and solve the equation for height.

    6. If bottom is auto and top and height are not, reset any auto values for margin-top and margin-bottom to 0 and solve the equation for bottom.

For replaced elements, the steps used to determine vertical layout are:

  1. Determine the value of height as described for inline replaced elements (see “Inline Layout”).

  2. If both top and bottom are set to auto, set top to the static top position.

  3. If the values are overconstrained, ignore the value for bottom and solve for that value.

Table Layout

The layout of tables can get quite complicated, especially because CSS defines two different ways to calculate table and cell widths, as well as two ways to handle the borders of tables and elements internal to the table. Figure 1-4 illustrates the components of a table.

Table Arrangement Rules

In general, a table is laid out according to the following principles:

  • Each row box encompasses a single row of grid cells. All of the row boxes in a table fill the table from top to bottom in the order they occur in the source document. Thus, the table contains as many grid rows as there are row elements.

  • A row group’s box encompasses the same grid cells as the row boxes that it contains.

  • A column box encompasses one or more columns of grid cells. Column boxes are placed next to each other in the order they occur. The first column box is on the left for left-to-right languages and on the right for right-to-left languages.

  • A column group’s box encompasses the same grid cells as the column boxes that it contains.

  • Although cells may span several rows or columns, CSS does not define how that happens. It is instead left to the document language to define spanning. Each spanned cell is a rectangular box one or more grid cells wide and high. The top row of this rectangle is in the row that is parent to the cell. The cell’s rectangle must be as far to the left as possible in left-to-right languages, but it may not overlap any other cell box. It must also be to the right of all cells in the same row that are earlier in the source document in a left-to-right language. In right-to-left languages, a spanned cell must be as far to the right as possible without overlapping other cells and must be to the left of all cells in the same row that come after it in the document source.

  • A cell’s box cannot extend beyond the last row box of a table or row group. If the table structure causes this condition, the cell must be shortened until it fits within the table or row group that encloses it.

Figure 1-4. Table layout components

Fixed Table Layout

The fixed-layout model is fast because its layout doesn’t depend on the contents of table cells; it’s driven by the width values of the table, columns, and cells within the first row of the table. The fixed-layout model uses the following simple steps:

  1. Any column element whose width property has a value other than auto sets the width for that column.

  2. If a column has an auto width, but the cell in the first row of the table within that column has a width other than auto, that cell sets the width for that column. If the cell spans multiple columns, the width is divided equally among the columns.

  3. Any columns that are still auto-sized are sized so that their widths are as equal as possible.

At that point, the width of the table is set to be either the value of width for the table or the sum of the column widths, whichever is greater. If the table turns out to be wider than the column widths, the difference is divided by the number of columns and added to each of them.

Automatic Table Layout

The automatic-layout model, although not as fast as the fixed-layout, is likely to be much more familiar to authors because it’s substantially the same model that HTML tables have used for years. In most current user agents, use of this model will be triggered by a table with a width of auto, regardless of the value of table-layout—although this is not assured.

The details of the model can be expressed in the following steps:

  1. For each cell in a column, calculate both the minimum and maximum cell width.

  2. Determine the minimum width required to display the content. In determining the minimum content width, the content can flow to any number of lines, but it may not stick out of the cell’s box. If the cell has a width value that is larger than the minimum possible width, the minimum cell width is set to the value of width. If the cell’s width value is auto, the minimum cell width is set to the minimum content width.

  3. For the maximum width, determine the width required to display the content without any line-breaking, other than that forced by explicit line-breaking (e.g., due to the <br> element). That value is the maximum cell width.

  4. For each column, calculate both the minimum and maximum column width.

    1. The column’s minimum width is determined by the largest minimum cell width of the cells within the column. If the column has been given an explicit width value that is larger than any of the minimum cell widths within the column, the minimum column width is set to the value of width.

    2. For the maximum width, take the largest maximum cell width of the cells within the column. If the column has been given an explicit width value that is larger than any of the maximum cell widths within the column, the maximum column width is set to the value of width. These two behaviors recreate the traditional HTML table behavior of forcibly expanding any column to be as wide as its widest cell.

  5. In cases where a cell spans more than one column, the sum of the minimum column widths must be equal to the minimum cell width for the spanning cell. Similarly, the sum of the maximum column widths must equal the spanning cell’s maximum width. User agents should divide any changes in column widths equally among the spanned columns.

In addition, the user agent must take into account that when a column width has a percentage value for its width, the percentage is calculated in relation to the width of the table—even though that width is not known yet. The user agent must hang on to the percentage value and use it in the next part of the algorithm. Once the user agent has determined how wide or narrow each column can be, it can calculate the width of the table. This happens as follows:

  1. If the computed width of the table is not auto, the computed table width is compared to the sum of all the column widths plus any borders and cell-spacing. (Columns with percentage widths are likely calculated at this time.) The larger of the two values is the final width of the table. If the table’s computed width is larger than the sum of the column widths, borders, and cell-spacing, all columns are increased in width by an equal amount so they fill the computed width of the table.

  2. If the computed width of the table is auto, the final width of the table is determined by summing up the column widths, borders, and cell-spacing. This means the table will be only as wide as needed to display its content, just as with traditional HTML tables. Any columns with percentage widths use that percentage as a constraint, but it is a constraint that a user agent does not have to satisfy.

Once the last step is completed, then (and only then) can the user agent actually lay out the table.

Collapsing Cell Borders

The collapsing cell model largely describes how HTML tables have always been laid out when they have no cell-spacing. The following rules govern this model:

  • Table elements cannot have any padding, although they can have margins. Thus, there is never separation between the border around the outside of the table and its outermost cells.

  • Borders can be applied to cells, rows, row groups, columns, and column groups. The table element itself can, as always, have a border.

  • There is never any separation between cell borders. In fact, borders collapse into each other where they adjoin so that only one of the collapsing borders is actually drawn. This is somewhat akin to margin-collapsing, where the largest margin wins. When cell borders collapse, the “most interesting” border wins.

  • Once they are collapsed, the borders between cells are centered on the hypothetical grid lines between the cells.

Collapsing borders

When two or more borders are adjacent, they collapse into each other, as shown in Figure 1-5. There are strict rules governing which borders will win and which will not:

  1. If one of the collapsing borders has a border-style of hidden, it takes precedence over all other collapsing borders: all borders at this location are hidden.

  2. If one of the collapsing borders has a border-style of none, it takes the lowest priority. There will be no border drawn at this location only if all of the borders meeting at this location have a value of none. Note that none is the default value for border-style.

  3. If at least one of the collapsing borders has a value other than either none or hidden, narrow borders lose out to wider ones. If two or more of the collapsing borders have the same width, the border style is taken in the following order, from most preferred to least: double, solid, dashed, dotted, ridge, outset, groove, inset. Thus, if two borders with the same width collapse and one is dashed while the other is outset, the border at that location will be dashed.

  4. If collapsing borders have the same style and width but differ in color, the color used is taken from an element in the following list, from most preferred to least: cell, row, row group, column, column group, table. Thus, if the borders of a cell and a column—identical in every way except color—collapse, the cell’s border color (and style and width) will be used. If the collapsing borders come from the same type of element—such as two row borders with the same style and width, but different colors—the one farthest to the left and top wins in left-to-right languages; in right-to-left languages, the cell farthest to the right and top wins.

Figure 1-5. Collapsing cell borders model

Vertical Alignment Within Cells

The following describes the detailed process for aligning cell contents within a row:

  1. If any of the cells are baseline-aligned, the row’s baseline is determined and the content of the baseline-aligned cells is placed.

  2. Any top-aligned cell has its content placed. The row now has a provisional height, which is defined by the lowest cell bottom of the cells that have already had their content placed.

  3. If any remaining cells are middle- or bottom-aligned, and the content height is taller than the provisional row height, the height of the row is increased by lowering the baseline in order to enclose the tallest of those cells.

  4. All remaining cells have their content placed. In any cell with contents shorter than the row height, the cell’s padding is increased in order to match the height of the row.

Get CSS Pocket Reference, 4th Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.