Skip to content

Latest commit

 

History

History
795 lines (541 loc) · 48.2 KB

File metadata and controls

795 lines (541 loc) · 48.2 KB

第 20 章 Media-Dependent 风格 Media-Dependent Styles

A great deal of our CSS work goes into putting content onto screens of various kinds,whether they sit on office desks or rest in the palms of our hands. There is more to the web than screens, though, and even in the part that is screen-centric, there are many different kinds of screens, each with its own constraints. Recognizing this reality, CSS provides a number of tools with which to apply styles in specific media, or in media with specific features.

我们的很多 CSS 工作都是将内容放到各种屏幕上,无论是放在办公桌上还是放在手掌上。然而,web 不仅仅是屏幕,即使在以屏幕为中心的部分,也有许多不同类型的屏幕,每种屏幕都有自己的约束。认识到这一事实后,CSS 提供了许多工具来在特定媒体或具有特定特性的媒体中应用样式。

20.1 Defining Media-Dependent Styles

Thanks to the mechanisms defined in HTML and CSS called media queries, you can restrict any kind of style sheet to a specific medium, such as screen or print, and set of media conditions. These mechanisms allows you to define a combination of media types and parameters such as display size or color depth, to pick two examples. We’ll cover the basic form of these queries before exploring the more complex forms.

得益于 HTML 和 CSS 中定义的“媒体查询”机制,您可以将任何类型的样式表限制为特定的媒体,如屏幕或打印,以及一组媒体条件。这些机制允许您定义媒体类型和参数(如显示大小或颜色深度)的组合,以选择两个示例。在研究更复杂的表单之前,我们将介绍这些查询的基本形式。

20.1.1 Basic Media Queries

For HTML-based style sheets, you can impose medium restrictions through the media attribute. This works the same for both the link and style elements:

对于基于 html 的样式表,可以通过 media 属性施加媒体限制。这同样适用于 linkstyle 元素:

<link rel="stylesheet" type="text/css" media="print" href="article-print.css" />
<style type="text/css" media="speech">
  body {
    font-family: sans-serif;
  }
</style>

The media attribute can accept a single medium value or a comma-separated list of values. Thus, to link in a style sheet that should be used in only the screen and speech media, you would write:

<link
  rel="stylesheet"
  type="text/css"
  media="screen, speech"
  href="visual.css"
/>

In a style sheet itself, you can also impose medium restrictions on @import rules:

@import url(visual.css) screen;
@import url(outloud.css) speech;
@import url(article-print.css) print;

Remember that if you don’t add medium information to a style sheet, it will be applied in all media. Therefore, if you want one set of styles to apply only on screen, and another to apply only in print, then you need to add medium information to both style sheets. For example:

<link
  rel="stylesheet"
  type="text/css"
  media="screen"
  href="article-screen.css"
/>
<link rel="stylesheet" type="text/css" media="print" href="article-print.css" />

If you were to remove the media attribute from the first link element in the preceding example, the rules found in the style sheet article-screen.css would be applied in all media.

CSS also defines syntax for @media blocks. This allows you define styles for multiple media within the same style sheet. Consider this basic example:

<style type="text/css">
  body {
    background: white;
    color: black;
  }
  @media screen {
    body {
      font-family: sans-serif;
    }
    h1 {
      margin-top: 1em;
    }
  }
  @media print {
    body {
      font-family: serif;
    }
    h1 {
      margin-top: 2em;
      border-bottom: 1px solid silver;
    }
  }
</style>

Here we see that in all media, the body element is given a white background and a black foreground by the first rule. This happens because its style sheet, the one defined by the style attribute, has no media attribute and thus defaults to all. Next, a block of rules is provided for the screen medium alone, followed by another block of rules that applies only in the print medium.

@media blocks can be any size, containing any number of rules. In situations where authors have control over a single style sheet, such as a shared hosting environment or a content management system (CMS) that restricts what users can edit, @media blocks may be the only way to define medium-specific styles. This is also the case in situations where CSS is used to style a document using an XML language that does not contain a media attribute or its equivalent.

These are the four most widely recognized media types:

all

Use in all presentational media.

print

Use when printing the document for sighted users, and also when displaying a print preview of the document.

screen

Use when presenting the document in a screen medium like a desktop computer monitor or a handheld device. All web browsers running on such systems are screen-medium user agents.

speech

Use in speech synthesizers, screen readers, and other audio renderings of the document.

HTML4 defined a list of media types that CSS originally recognized, but most of them have been deprecated and should be avoided. These are aural, braille, embossed, handheld, projection, tty, and tv. If you have old style sheets that use these media types, they should be converted to one of the four recognized media types, if possible.

It’s entirely possible that new media types will be added over time, so remember that this limited list may not always be so limited. It’s fairly easy to imagine augmented-reality as a media type, for example, since text in AR displays would likely need to be of higher contrast in order to stand out against the background reality.

It’s possible in some circumstances to combine media types into comma-separated lists, though the rationale for doing so isn’t terribly compelling, given the small number of media types currently available. For example, styles could be restricted to only screen and print media in the following ways:

<link
  rel="stylesheet"
  type="text/css"
  media="screen, print"
  href="article.css"
/>
@import url(article.css) print, screen;
@media screen, print {
  /* styles go here */
}

20.1.2 Complex Media Queries

In the previous section, we saw how multiple media types could be chained together with a comma. We might call that a compound media query, because it allows us to address multiple media at once. There is a great deal more to media queries, though: it’s possible to apply styles based not just media types, but also features of those media, such as display size or color depth.

This is a great deal of power, and it’s not enough to rely on commas to make it all happen. Thus, CSS introduced the logical operator and to pair media types with features of those media.

Let’s see how this plays out in practice. Here are two essentially equivalent ways of applying an external style sheet when rendering the document on a color printer:

<link
  href="print-color.css"
  type="text/css"
  media="print and (color)"
  rel="stylesheet"
/>
@import url(print-color.css) print and (color);

Anywhere a media type can be given, a media query can be constructed. This means that, following on the examples of the previous section, it is possible to list more than one query in a comma-separated list:

<link
  href="print-color.css"
  type="text/css"
  media="print and (color), screen and (color)"
  rel="stylesheet"
/>
@import url(print-color.css) print and (color), screen and (color);

In a situation where even one of the media queries evaluates to true, the associated style sheet is applied. Thus, given the previous @import, print-color.css will be applied if rendering to a color printer or to a color screen environment. If printing on a blackand-white printer, both queries will evaluate to false and print-color.css will not be applied to the document. The same holds true in a grayscale screen environment, any speech media environment, and so forth.

Each media descriptor is composed of a media type and one or more listed media features, with each media feature descriptor is enclosed in parentheses. If no media type is provided, then it is assumed to be all, which makes the following two examples equivalent:

@media all and (min-resolution: 96dpi) {…}
@media (min-resolution: 960dpi) {…}

Generally speaking, a media feature descriptor is formatted like a property-value pair in CSS, only enclosed by parentheses. There are a few differences, most notably that some features can be specified without an accompanying value. For example, any color-based medium will be matched using (color), whereas any color medium using a 16-bit color depth is matched using (color: 16). In effect, the use of a descriptor without a value is a true/false test for that descriptor: (color) means “is this medium in color?”

Multiple feature descriptors can be linked with the and logical keyword. In fact, there are two logical keywords in media queries:

and

Links together two or more media features in such a way that all of them must be true for the query to be true. For example, (color) and (orientation: land scape) and (min-device-width: 800px) means that all three conditions must be satisfied: if the media environment has color, is in landscape orientation, and the device’s display is at least 800 pixels wide, then the style sheet is used.

not

Negates the entire query so that if all of the conditions are true, then the style sheet is not applied. For example, not (color) and (orientation: landscape) and (min-device-width: 800px) means that if the three conditions are satisfied, the statement is negated. Thus, if the media environment has color, is in landscape orientation, and the device’s display is at least 800 pixels wide, then the style sheet is not used. In all other cases, it will be used.

Note that the not keyword can only be used at the beginning of a media query. It is not presently legal to write something like (color) and not (min-device-width: 800px). In such cases, the query will be ignored. Note also that browsers too old to understand media queries will always skip a style sheet whose media descriptor starts with not.

An example of how all this plays out is shown in Figure 20-1, which is the result of the following styles:

@media screen and (min-resolution: 72dpi) {
  .cl01 {
    font-style: italic;
  }
}
@media screen and (min-resolution: 32767dpi) {
  .cl02 {
    font-style: italic;
  }
}
@media not print {
  .cl03 {
    font-style: italic;
  }
}
@media not print and (grayscale) {
  .cl04 {
    font-style: italic;
  }
}

Logical operators in media queries

First, bear in mind that, even though you may be reading this on printed paper, the actual image in Figure 20-1 was generated with a screen-medium browser (Firefox Nightly, as it happens) displaying an HTML document with the previous CSS applied to it. So everything you see there was operating under a screen medium.

The first line is italicized because the screen on which the file was displayed had a resolution equal to or greater than than 72 dots per inch. Its resolution was not, however, 32767dpi or higher, so the second media block is skipped and thus the second line stays un-italicized. The third line is italicized because, being a screen display, it was not print. The last line is italicized because it was either not print or not grayscale—in this case, not grayscale.

There is no OR keyword for use in media queries. Instead, the commas that separate a list of queries serve the function of an OR—screen, print means “apply if the media is screen or print.” Therefore, instead of screen and (max-color: 2) or (monochrome), which is invalid and thus ignored, you need to write screen and (max-color: 2), screen and (monochrome).

There is one more keyword, only, which is designed to create deliberate backward incompatibility. Yes, really.

only

Used to hide a style sheet from browsers too old to understand media queries. For example, to apply a style sheet in all media, but only in those browsers that understand media queries, you write something like @import url(new.css) only all. In browsers that do understand media queries, the only keyword is ignored and the style sheet is applied. In browsers that do not understand media queries, the only keyword creates an apparent media type of only all, which is not valid. Thus, the style sheet is not applied in such browsers. Note that the only keyword can only be used at the beginning of a media query.

Media feature descriptors

So far we’ve seen a number of media feature descriptors in the examples, but not a complete list of the possible descriptors and their values. Let us fix that now! Note that none of the following values can be negative, and remember that feature descriptors are always enclosed in parentheses.

Descriptors: width, min-width, max-width

Values: <length>

Refers to the width of the display area of the user agent. In a screen-media web browser, this is the width of the viewport plus any scrollbars. In paged media, this is the width of the page box, which is the area of the page in which content is rendered. Thus, (min-width: 850px) applies when the viewport is greater than or equal to 850 pixels wide.

Descriptors: height, min-height, max-height

Values: <length>

Refers to the height of the display area of the user agent. In a screen-media web browser, this is the height of the viewport plus any scrollbars. In paged media, this is the height of the page box. Thus, (height: 567px) applies when the viewport’s height is precisely 567 pixels tall.

Descriptors: device-width, min-device-width, max-device-width

Values: <length>

Refers to the width of the complete rendering area of the output device. In screen media, this is the width of the screen; i.e., a handheld device screen’s or desktop monitor’s horizontal measurement. In paged media, this is the width of the page itself. Thus, (max-device-width: 1200px) applies when the device’s output area is less or equal to than 1,200 pixels wide.

Descriptors: device-height, min-device-height, max-device-height

Values: <length>

Refers to the height of the complete rendering area of the output device. In screen media, this is the height of the screen; i.e., a handheld device screen’s or desktop monitor’s vertical measurement. In paged media, this is the height of the page itself. Thus, (max-device-height: 400px) applies when the device’s output area is less than or equal to 400 pixels tall.

Descriptors: aspect-ratio, min-aspect-ratio, max-aspect-ratio

Values: <ratio>

Refers to the ratio that results from comparing the width media feature to the height media feature (see the definition of <ratio> in the next section). Thus, (min-aspect-ratio: 2/1) applies to any viewport whose width-toheight ratio is at least 2:1.

Descriptors: device-aspect-ratio, min-device-aspect-ratio, max-deviceaspect-ratio

Values: <ratio>

Refers to the ratio that results from comparing the device-width media feature to the device-height media feature (see the definition of <ratio> in the next section). Thus, (device-aspect-ratio: 16/9) applies to any output device whose display area width-to-height is exactly 16:9.

Descriptors: color, min-color, max-color

Values: <integer>

Refers to the presence of color-display capability in the output device, with an optional number value representing the number of bits used in each color components. Thus, (color) applies to any device with any color depth at all, whereas (min-color: 4) means there must be at least four bits used per color component. Any device that does not support color will return 0.

Descriptors: color-index, min-color-index, max-color-index

Values: <integer>

Refers to the total number of colors available in the output device’s color lookup table. Any device that does not use a color lookup table will return 0. Thus, (min-color-index: 256) applies to any device with a minimum of 256 colors available.

Descriptors: monochrome, min-monochrome, max-monochrome

Values: <integer>

Refers to the presence of a monochrome display, with an optional number of bits-per-pixel in the output device’s frame buffer. Any device that is not monochrome will return 0. Thus, (monochrome) applies to any monochrome output device, whereas (min-monochrome: 2) means any monochrome output device with a minimum of 2 bits per pixel in the frame buffer.

Descriptors: resolution, min-resolution, max-resolution

Values: <resolution>

Refers to the resolution of the output device in terms of pixel density, measured in either dots per inch (dpi) or dots per centimeter (dpcm); see the definition of <resolution> in the next section for details. If an output device has pixels that are not square, then the least dense axis is used; for example, if a device is 100 dpcm along one axis and 120 dpcm along the other, then 100 is the value returned. Additionally, in such non-square cases, a bare resolution feature query—that is, one without a value—can never match (though min-resolution and max-resolution can). Note that resolution values must not only be non-negative, but also nonzero.

Descriptors: orientation

Values: portrait | landscape

Refers to the orientation of the user agent’s display area, where portrait is returned if the media feature height is equal to or greater than the media feature width. Otherwise, the result is landscape.

Descriptor: scan

Values: progressive | interlace

Refers to the scanning process used in an output device. interlace is the type generally used in CRT and some plasma displays. progressive is more common, being the type of scanning used in most modern displays.

Descriptor: grid

Values: 0 | 1

Refers to the presence (or absence) of a grid-based output device, such as a TTY terminal. A grid-based device will return 1; otherwise, 0 is returned. This feature descriptor can be used in place of the old tty media descriptor. New value types

There are two new value types introduced by media queries, and which (as of early 2017. are not used in any other context. These types are used in conjunction with specific media features, which are explained in the previous sections:

<ratio>

A ratio value is two positive <integer> values separated by a solidus (/) and optional whitespace. The first value refers to the width, and the second to the height. Thus, to express a height-to-width ratio of 16:9, you can write 16/9 or 16 / 9. As of this writing, there is no facility to express a ratio as a single real number, nor to use a colon separator instead of a solidus.

<resolution>

A resolution value is a positive <integer> followed by either of the unit identifiers dpi or dpcm. In CSS terms, a “dot” is any display unit, the most familiar of which is the pixel. As usual, whitespace is not permitted between the <integer> and the identifier. Therefore, a display whose display has exactly 150 pixels (dots) per inch is matched with 150dpi.

Responsive styling

Media queries are, at least as of early 2017, the foundation on which the practice of responsive web design is built. By applying different sets of rules depending on the display environment, it’s possible to marry mobile-friendly and desktop-friendly styles into a single style sheet.

Those terms were put in quote because, as you may have seen in your own life, the lines between what’s mobile and what’s desktop are blurred. A laptop with a touchsensitive screen that folds all the way back can act as both a tablet and a laptop, for example. CSS doesn’t (yet) have a way of detecting whether or not a hinge is open past a certain point, nor whether the device is held in hand or sitting on a flat surface. Instead, inferences are drawn from aspects of the media environment, like display size or display orientation.

A fairly common pattern in responsive design is to define breakpoints for each @media block. This often takes the form of certain pixel widths, like this:

/* …common styles here… */
@media (max-width: 400px) {
  /* …small-screen styles here… */
}
@media (min-width: 401px) and (max-width: 1000px) {
  /* …medium-screen styles here… */
}
@media (min-width: 1001px) {
  /* …big-screen styles here… */
}

This is often sufficient. It does make certain assumptions about what a device can display and how it will report that, however. For example, the iPhone 6 Plus had a resolution of 1,242 × 2,208, which it downsampled to 1,080 × 1,920. Even at the downsampled resolution, that’s enough pixels across to qualify for big-screen styles in the previous example.

But wait! The iPhone 6 Plus also maintained an internal coordinate system of points which measured 414 × 736. If it decided to use those as its definition of pixels, which would be entirely valid, then it would only get the small-screen styles. The point here isn’t to single out the iPhone 6 Plus as uniquely bad, which it wasn’t, but to illustrate the uncertainties of relying on pixel-based media queries. Browser makers have gone to some effort to make their browsers behave with some semblance of sanity, but never quite as much as we’d like, and you never know when a new device’s assumptions will clash with your own.

There are other methods available, though they come with their own uncertainties. Instead of pixels, you might try em-based measures, something like this:

/* …common styles here… */
@media (max-width: 20em) {
  /* …small-screen styles here… */
}
@media (min-width: 20.01em) and (max-width: 50em) {
  /* …medium-screen styles here… */
}
@media (min-width: 50.01em) {
  /* …big-screen styles here… */
}

This ties the breakpoints to text display size rather than pixels, which is somewhat more robust. This isn’t perfect either, though: it relies on a sensible approach to determining the em width of, say, a smartphone. It also directly relies on the actual font-family and size used by the device, which varies from one device to another. Here’s another seemingly simple query set with potentially surprising results:

/* …common styles here… */
@media (orientation: landscape) {
  /* …wider-than-taller styles here… */
}
@media (orientation: portrait) {
  /* …taller-than-wider styles here… */
}

This feels like a good way to tell if a smartphone is in use: after all, most of them are taller than they are wide, and most people don’t turn them sideways to read. The wrinkle is that the orientation feature refers to the height and width descriptors; that is, orientation is portrait is height is equal to or larger than width. Not device-height and device-width, but height and width, which refer to the display area of the user agent.

That means a desktop browser window whose display area (the part inside the browser Chrome) is taller than it is wide, or even perfectly square, will get the portrait styles. So if you assume “portrait equals smartphone,” some of your desktop users could get a surprise.

The basic point here is: responsive styling is powerful, and like any powerful tool, it requires a fair amount of thought and care in its use. Carefully considering the implications of each combination of feature queries is the minimum requirement for successful responsiveness.

20.2 Paged Media

In CSS terms, a paged medium is any medium where a document’s presentation is handled as a series of discrete “pages.” This is different than the screen, which is a continuous medium: documents are presented as a single, scrollable “page.” An analog example of a continuous medium is a papyrus scroll. Printed material, such as books, magazines, and laser printouts, are all paged media. So too are slideshows, where a series of slides are shown one at a time. Each slide is a “page” in CSS terms.

20.2.1 Print Styles

Even in the paperless future, the most commonly encountered paged medium is a printout of some document—a web page, a word-processing document, a spread‐sheet, or something else that has been committed to the thin wafers of a dead tree. Authors can do a number of things to make printouts of their documents more pleasing for the user, from affecting page-breaking to creating styles meant specifically for print.

Note that print styles would also be applied to document display in a print preview mode. Thus, it’s possible in some circumstances to see print styles on a monitor.

Differences between screen and print

Beyond the obvious physical differences, there are a number of stylistic differences between screen and print design. The most basic involves font choices. Most designers will tell you that sans-serif fonts are best suited for screen design, but serif fonts are more readable in print. Thus, you might set up a print style sheet that uses Times instead of Verdana for the text in your document.

Another major difference involves font sizing. If you’ve spent any time at all doing web design, you’ve probably heard again and again (and again) that points are a horrible choice for font sizing on the web. This is basically true, especially if you want your text to be consistently sized between browsers and operating systems. However, print design is not web design any more than web design is print design. Using points, or even centimeters or picas, is perfectly OK in print design because printing devices know the physical size of their output area. If a printer has been loaded with 8.5 × 11 inch paper, then it knows it has a printing area that will fit within the edges of a piece of paper. It also knows how many dots there are in an inch, since it knows the dpi it’s capable of generating. This means that it can cope with physical-world length units like points.

Many a print style sheet has started with:

body {
  font: 12pt "Times New Roman", "TimesNR", Times, serif;
}

It’s so traditional, it just might bring a tear of joy to the eye of a graphic artist reading over your shoulder. But make sure they understand that points are acceptable only because of the nature of the print medium—they’re still not good for web design. Alternatively, the lack of backgrounds in most printouts might bring a tear of frustration to that designer’s eye. In order to save users ink, most web browsers are preconfigured not to print background colors and images. If the user wants to see those backgrounds in the printout, they have to change an option somewhere in the preferences.

CSS can’t do anything to force the printing of backgrounds. However, you can use a print style sheet to make backgrounds unnecessary. For example, you might include this rule in your print style sheet:

* {
  color: black !important;
  background: transparent !important;
}

This will do its utmost to ensure all of your elements print out as black text and remove any backgrounds you might have assigned in an all-medium style sheet. It also makes sure that if you have a web design that puts yellow text on a dark gray background, a user with a color printer won’t get yellow text on a white piece of paper.

One other difference between paged media and continuous media is that multicolumn layouts are even harder to use in paged media. Suppose you have an article where the text has been formatted as two columns. In a printout, the left side of each page will contain the first column, and the right side the second. This would force the user to read the left side of every page, then go back to the beginning of the printout and read the right side of every page. This is annoying enough on the web, but on paper it’s much worse.

One solution is to use CSS for laying out your two columns (by floating them, perhaps) and then writing a print style sheet that restores the content to a single column. Thus, you might write something like this for the screen style sheet:

div#leftcol {
  float: left;
  width: 45%;
}
div#rightcol {
  float: right;
  width: 45%;
}

Then in your print style sheet, you would write:

div#leftcol,
div#rightcol {
  float: none;
  width: auto;
}

Alternatively, in user agents that support it, you might define actual multicolumn layout for both screen and print, and trust the user agents to do the right thing. We could spend an entire chapter on the details of print design, but that really isn’t the purpose of this book. Let’s start exploring the details of paged-media CSS and leave the design discussions for another book.

Defining the page size

In much the same way as it defines the element box, CSS2 defines a page box that describes the components of a page. A page box is composed of basically two regions:

  • The page area, which is the portion of the page where the content is laid out. This is roughly analogous to the content area of a normal element box, to the extent that the edges of the page area act as the initial containing block for layout within a page.
  • The margin area, which surrounds the page area.

The page box model is illustrated in Figure 20-2.

The page box

The @page block is the method by which settings are made, and the size property is used to define the actual dimensions of the page box. Here’s a simple example:

@page {
  size: 7.5in 10in;
  margin: 0.5in;
}

@page is a block like @media is a block, and within it can contain any set of styles. One of them, size, only makes sense in the context of an @page block.

As of early 2017, only Chrome and Opera supported size, the latter with some oddities in its calculation of dimensions.

This property is used to define the size of the page area. The value landscape is meant to cause the layout to be rotated 90 degrees, whereas portrait is the normal orientation for Western-language printing. Thus, an author could cause a document to be printed sideways by declaring the following, with the result shown in Figure 20-3:

@page {
  size: landscape;
}

Landscape page sizing

In addition to landscape and portrait, there are a number of predefined page-size keywords available. These are summarized in Table 20-1.

// T20-1

Any one of the keywords can be used to declare a page size. The following defines a page to be JIS B5 size:

@page {
  size: JIS-B5;
}

These keywords can be combined with the landscape and portrait keywords; thus, to define landscape-oriented North American legal pages, the following is used:

@page {
  size: landscape legal;
}

Besides using keywords, it’s also possible to define page sizes using length units. In such cases, the width is given first, and then the height. Therefore, the following defines a page area 8 inches wide by 10 inches tall:

@page {
  size: 8in 10in;
}

The defined area is usually centered within the physical page, with equal amounts of whitespace on each side. If the defined size is larger than the printable area of the page, then the user agent has to decide what to do to resolve the situation. There is no defined behavior here, so it’s really dealer’s choice.

Page margins and padding

Related to size, CSS includes the ability to style the margin area of the page box. If you want to make sure that only a small bit at the center of every 8.5 × 11 inch page is used to print, you could write:

@page {
  margin: 3.75in;
}

This would leave a printing area 1 inch wide by 3.5 inches tall. It is possible to use the length units em and ex to describe either the margin area or the page area, at least in theory. The size used is taken from the page context’s font, which is to say, the base font size used for the content displayed on the page.

The ability to set page margins and padding was barely supported as of early 2017. In Chrome, for example, attempting to define page margins caused the entire @page block to be ignored.

Selecting page types

CSS2 offers the ability to create different page types using named @page rules. Let’s say you have a document on astronomy that is several pages long, and in the middle of it, there is a fairly wide table containing a list of the physical characteristics of all the moons of Saturn. You want to print out the text in portrait mode, but the table needs to be landscape. Here’s how you’d start:

@page normal {
  size: portrait;
  margin: 1in;
}
@page rotate {
  size: landscape;
  margin: 0.5in;
}

Now you just need to apply these page types as needed. The table of Saturn’s moons has an id of moon-data, so you write the following rules:

body {
  page: normal;
}
table#moon-data {
  page: rotate;
}

This causes the table to be printed landscape, but the rest of the document to be in portrait orientation. The property page is what makes this possible.

As you can see from looking at the value definition, the whole reason page exists is to let you assign named page types to various elements in your document.

As of early 2017, there was little if any support for named pages.

There are more generic page types that you can address through special pseudoclasses. :first lets you apply special styles to the first page in the document. For example, you might want to give the first page a larger top margin than other pages.

Here’s how:

@page {
  margin: 3cm;
}
@page :first {
  margin-top: 6cm;
}

This will yield a 3 cm margin on all pages, with the exception of a 6 cm top margin on the first page.

In addition to styling the first page, you can also style left and right pages, emulating the pages to the left and right of a book’s spine. You can style these differently using :left and :right. For example:

@page :left {
  margin-left: 3cm;
  margin-right: 5cm;
}
@page :right {
  margin-left: 5cm;
  margin-right: 3cm;
}

These rules will have the effect of putting larger margins between the content of the left and right pages, on the sides where the spine of a book would be. This is a common practice when pages are to be bound together into a book of some type.

As of early 2017, there was little if any support for :first, :left, or :right.

Page-breaking

In a paged medium, it’s a good idea to exert some influence over how page breaks are placed. You can affect page breaking using the properties page-break-before and page-break-after, both of which accept the same set of values.

The default value of auto means that a page break is not forced to come before or after an element. This is the same as any normal printout. always causes a page break to be placed before (or after) the styled element.

For example, assume a situation where the page title is an h1 element, and the section titles are all h2 elements. We might want a page break right before the beginning of each section of a document and after the document title. This would result in the following rules, illustrated in Figure 20-4:

h1 {
  page-break-after: always;
}
h2 {
  page-break-before: always;
}

Inserting page breaks

If you want the document title to be centered in its page, then we’d add rules to that effect. Since we don’t, we just get a very straightforward rendering of each page. The values left and right operate in the same manner as always except they further define the type of page on which printing can resume. Consider the following:

h2 {
  page-break-before: left;
}

This will force every h2 element to be preceded by enough page breaks so that the h2 will be printed at the top of a left page—that is, a page surface that would appear to the left of a spine if the output were bound. In double-sided printing, this would mean printing on the back of a piece of paper.

So let’s assume that, in printing, the element just before an h2 is printed on a right page. The previous rule would cause a single page break to be inserted before the h2, thus pushing it to the next page. If the next h2 is preceded by an element on a left page, however, the h2 would be preceded by two page breaks, thus placing it at the top of the next left page. The right page between the two would be intentionally left blank. The value right has the same basic effect, except it forces an element to be printed at the top of a right page preceded by either one or two page breaks.

The companion to always is avoid, which directs the user agent to do its best to avoid placing a page break either before or after an element. To extend the previous example, suppose you have subsections whose titles are h3 elements. You want to keep these titles together with the text that follows them, so you want to avoid a page break following an h3 whenever possible:

h3 {
  page-break-after: avoid;
}

Note, though, that the value is called avoid, not never. There is no way to absolutely guarantee that a page break will never be inserted before or after a given element.

Consider the following:

img {
  height: 9.5in;
  width: 8in;
  page-break-before: avoid;
}
h4 {
  page-break-after: avoid;
}
h4 + img {
  height: 10.5in;
}

Now, suppose further that you have a situation where an h4 is placed between two images, and its height calculates to be half an inch. Each image will have to be printed on a separate page, but there are only two places the h4 can go: at the bottom of the page holding the first element, or on the page after it. If it’s placed after the first image, then it has to be followed by a page break, since there’s no room for the second image to follow it.

On the other hand, if the h4 is placed on a new page following the first image, then there won’t be room on that same page for the second image. So, again, there will be a page break after the h4. And, in either case, at least one image, if not both, will be preceded by a page break. There’s only so much the user agent can do, given a situation like this one.

Situations such as these are rare, but they can happen—for example, in a case where a document contains nothing but tables preceded by headings. There may be cases where tables print in such a way that they force a heading element to be followed by a page break, even though the author requested such break placement be avoided.

The same sorts of issues can arise with the other page-break property, page-breakinside. Its possible values are more limited than those of its cousins.

With page-break-inside, you pretty much have one option other than the default: you can request that a user agent try to avoid placing page breaks within an element. If you have a series of aside divisions, and you don’t want them broken across two pages, you could declare:

div.aside {
  page-break-inside: avoid;
}

Again, this is a suggestion more than an actual rule. If an aside turns out to be longer than a page, the user agent can’t help but place a page break inside the element.

Orphans and widows

In an effort to provide finer influence over page-breaking, CSS2 defines two properties common to both traditional print typography and desktop publishing: widows and orphans.

These properties have similar aims but approach them from different angles. The value of widows defines the minimum number of line boxes found in an element that can be placed at the top of a page without forcing a page break to come before the element. orphans has the same effect in reverse: it gives the minimum number of line boxes that can appear at the bottom of a page without forcing a page break before the element.

Let’s take widows as an example. Suppose you declare:

p {
  widows: 4;
}

This means that any paragraph can have no fewer than four line boxes appear at the top of a page. If the layout of the document would lead to fewer line boxes, then the entire paragraph is placed at the top of the page. Consider the situation shown in Figure 20-5. Cover up the top part of the figure with your hand so that only the second page is visible. Notice that there are two line boxes there, from the end of a paragraph that started on the previous page. Given the default widows value of 2, this is an acceptable rendering. However, if the value were 3 or higher, the entire paragraph would appear at the top of the second page as a single block. This would require that a page break be inserted before the paragraph in question.

Counting the widows and orphans

Refer back to Figure 20-5, and this time cover up the second page with your hand.

Notice the four line boxes at the bottom of the page, at the beginning of the last paragraph. This is fine as long as the value of orphans is 4 or less. If it were 5 or higher, then the paragraph would again be preceded by a page break and be laid out as a single block at the top of the second page.

One potential pitfall is that both orphans and widows must be satisfied. If an author declared the following, then most paragraphs would be without an interior page break:

p {
  widows: 30;
  orphans: 30;
}

It would take a pretty lengthy paragraph to allow an interior page break, given those values. If the intent is to prevent interior breaking, then that intent would be better expressed as:

p {
  page-break-inside: avoid;
}

Page-breaking behavior

Because CSS2 allows for some odd page-breaking styles, it defines a set of behaviors regarding allowed page breaks and “best” page breaks. These behaviors serve to guide user agents in how they should handle page-breaking in various circumstances.

There are really only two generic places where page breaks are permitted. The first of these is between two block-level boxes. If a page break falls between two block boxes, then the margin-bottom value of the element before the page break is reset to 0, as is the margin-top of the element following the page break. However, there are two rules that affect whether a page break can fall between two element boxes:

If the value of page-break-after for the first element—or the value of page-breakbefore for the second element—is always, left, or right, then a page break will be placed between the elements. This is true regardless of the value for the other element, even if it’s avoid. (This is a forced page break.)

If the value of the first element’s page-break-after value is auto, and the same is true for the second element’s page-break-before value, and they do not share an ancestor element whose page-break-inside value is not avoid, then a page break may be placed between them.

Figure 20-6 illustrates all the possible page-break placements between elements in a hypothetical document. Forced page breaks are represented as a filled square, whereas potential (unforced) page breaks are shown as an open square.

Second, page breaks are allowed between two line boxes inside a block-level box. This, too, is governed by a pair of rules:

  • A page break may appear between two line boxes only if the number of line boxes between the start of the element and the line box before the page break would be less than the value of orphans for the element. Similarly, a page break can be placed only where the number of line boxes between the line box after the page break and the end of the element is less than the value of widows.
  • A page break can be placed between line boxes if the value of page-breakinside for the element is not avoid.

Potential page-break placement between block boxes

In both cases, the second of the two rules controlling page-break placement is ignored if no page-break placement can satisfy all the rules. Thus, given a situation where an element has been given page-break-inside: avoid but the element is longer than a full page, a page break will be permitted inside the element, between two line boxes. In other words, the second rule regarding page-break placement between line boxes is ignored.

If ignoring the second rule in each pair of rules still does not yield good page-break placement, then other rules can also be ignored. In such a situation, the user agent is likely to ignore all page-break property values and proceed as if they were all auto, although this approach is not defined (or required) by the CSS specification.

In addition to the previously explored rules, CSS2 defines a set of best page-breaking behaviors:

  • Break as few times as possible.
  • Make all pages that don’t end with a forced break appear to have about the same height.
  • Avoid breaking inside a block that has a border.
  • Avoid breaking inside a table.
  • Avoid breaking inside a floated element.

These recommendations aren’t required of user agents, but they offer logical guidance that should lead to ideal page-breaking behaviors.

Repeated elements

A very common desire in paged media is the ability to have a running head. This is an element that appears on every page, such as the document’s title or the author’s name. This is possible in CSS2 by using a fixed-position element:

div#runhead {
  position: fixed;
  top: 0;
  right: 0;
}

This will place any div with an id of runhead at the top-right corner of every page box when the document is output to a paged medium. The same rule would place the element in the top-right corner of the viewport in a continuous medium, such as a web browser. Any element positioned in this way will appear on every page. It is not possible to copy an element to become a repeated element. Thus, given the following, the h1 element will appear as a running head on every page including the first one:

h1 {
  position: fixed;
  top: 0;
  width: 100%;
  text-align: center;
  font-size: 80%;
  border-bottom: 1px solid gray;
}

The drawback is that the h1 element, being positioned on the first page, cannot be printed as anything except the running head.

Elements outside the page

All this talk of positioning elements in a paged medium leads to an interesting question: what happens if an element is positioned outside the page box? You don’t even need positioning to create such a situation. Think about a pre element that contains a line with 411 characters. This is likely to be wider than any standard piece of paper, and so the element will be wider than the page box. What will happen then?

As it turns out, CSS2 doesn’t say exactly what user agents should do, so it’s up to each to come up with a solution. For a very wide pre element, the user agent might clip the element to the page box and throw away the rest of the content. It could also generate extra pages to display the leftover part of the element.

There are a few general recommendations for handling content outside the page box, and two that are really important. First, content should be allowed to protrude slightly from a page box in order to allow bleeding. This implies that no extra page would be generated for the portions of such content that exceed the page box, but do not extend all the way off the page.

Second, user agents are cautioned not to generate large numbers of empty pages for the sole purpose of honoring positioning information. Consider:

h1 {
  position: absolute;
  top: 1500in;
}

Assuming that the page boxes are 10 inches high, the user agent would have to precede an h1 with 150 page breaks (and thus 150 blank pages) just to honor that rule. Instead, a user agent might choose to skip the blank pages and just output the last one, which actually contains the h1 element.

The other two recommendations state that user agents should not position elements in strange places just to avoid rendering them, and that content placed outside a page box can be rendered in any of a number of ways. (Some of the commentary in CSS is useful and intriguing, but some seems to exist solely to cheerily state the obvious.)

20.3 Summary

Thanks to the combination of media queries and media-specific style features, it is possible to provide a wide range of design experiences from within a single set of styles. Whether reorganizing a page to account for varying display sizes or reworking the color scheme to support grayscale printing, authors have the ability to do a great deal to make their work the best in can be, no matter what the output channel.