Skip to content

Latest commit

 

History

History
704 lines (448 loc) · 57.9 KB

File metadata and controls

704 lines (448 loc) · 57.9 KB

第 10 章 浮动和形状 Floating and Shapes

For a very long time, floated elements were the basis of all our web layout schemes. (This is largely because of the property clear, which we’ll get to in a bit.) But floats were never meant for layout; their use as a layout tool was a hack nearly as egregious as the use of tables for layout. They were just what we had.

很长一段时间以来,浮动元素是我们所有网页布局方案的基础。(这在很大程度上是因为属性 clear,我们稍后会讲到。)但是浮动从来就不是用来布局的;它们作为布局工具的使用,与使用表格进行布局一样令人震惊。它们正是我们所拥有的。

Floats are quite interesting and useful in their own right, however, especially given the recent addition of float shaping, which allows the creation of nonrectangular shapes past which content can flow.

然而,浮动本身是非常有趣和有用的,特别是考虑到最近添加的浮动 shaping,它允许创建非矩形的形状,内容可以通过这些形状流动。

10.1 Floating

You are likely acquainted with the concept of floated elements. Ever since Netscape 1.1, it has been possible to float images by declaring, for instance, <img src="b5.gif" align="right">. This causes an image to float to the right and allows other content (such as text) to “flow around” the image. The name “floating,” in fact, comes from the Netscape DevEdge page “Extensions to HTML 2.0,” which stated:

您可能已经熟悉了浮动元素的概念。从 Netscape 1.1 开始,就可以通过声明(例如 <img src="b5.gif" align="right">。这将导致图像向右浮动,并允许其他内容(如文本)围绕图像“流动”。实际上,“浮动”这个名字来自 Netscape DevEdge 的“扩展到 HTML 2.0”页面,它是这样说的:

The additions to your ALIGN options need a lot of explanation. First, the values “left” and “right”. Images with those alignments are an entirely new floating image type.

添加到对齐选项需要大量的解释。首先,值“左”和“右”。具有这些对齐的图像是一种全新的浮动图像类型。

In the past, it was only possible to float images and, in some browsers, tables. CSS, on the other hand, lets you float any element, from images to paragraphs to lists. In CSS, this behavior is accomplished using the property float.

在过去,只能在某些浏览器中浮动图像和表。另一方面,CSS 允许您浮动任何元素,从图像到段落到列表。在 CSS 中,此行为是使用属性 float 来完成的。

For example, to float an image to the left, you could use this markup:

若想把图像浮动到左侧,可以使用以下标记:

<img src="b4.gif" style="float: left;" alt="b4" />

As Figure 10-1 makes clear, the image “floats” to the left side of the browser window and the text flows around it. This is just what you should expect.

如图10-1所示,这个图像浮动到浏览器窗口左边,文本则围绕图像周围,这正是我们想要的。

A floating image一个浮动的图像

However, when floating elements in CSS, some interesting issues come up.

然而,在CSS中浮动元素一些有趣的麻烦也随之而来。

10.1.1 浮动的元素 Floated Elements

Keep a few things in mind with regard to floating elements. In the first place, a floated element is, in some ways, removed from the normal flow of the document, although it still affects the layout. In a manner utterly unique within CSS, floated elements exist almost on their own plane, yet they still have influence over the rest of the document.

浮动元素后要注意几件事。首先,从某种程度上来说,虽然浮动元素仍然影响布局,但它已经脱离了常规的文档流。使用CSS浮动元素独特之处在于尽管浮动元素算是处在单独的平面上,但仍然会对文档其他内容产生影响。

This influence derives from the fact that when an element is floated, other content “flows around” it. This is familiar behavior with floated images, but the same is true if you float a paragraph, for example. In Figure 10-2, you can see this effect quite clearly, thanks to the margin added to the floated paragraph:

以上影响是因为元素浮动后,其他内容将环绕其产生流动。这是浮动图像的常见行为,不过即便是浮动段落,也是如此。在图10-2中你可以清晰的看到这一点,多亏浮动的段落加上了外边距:

p.aside {
  float: right;
  width: 15em;
  margin: 0 1em 1em;
  padding: 0.25em;
  border: 1px solid;
}

A floating paragraph 一个浮动段落

One of the first interesting things to notice about floated elements is that margins around floated elements do not collapse. If you float an image with 20-pixel margins, there will be at least 20 pixels of space around that image. If other elements adjacent to the image—and that means adjacent horizontally and vertically—also have margins, those margins will not collapse with the margins on the floated image, as you can see in Figure 10-3:

第一个值得注意的关于浮动元素有趣的事情是浮动元素四周的外边距不折叠。如果你为浮动图像设置20像素的外边距,图像周围至少有20像素的空白。如果与图像相邻(横向和纵向)的其他元素也有外边距,那么这些外边距不会与浮动图像的外边距折叠在一起,如图10-3所示。

p img {
  float: left;
  margin: 25px;
}

Floating images with margins 有外边距的浮动图像

If you do float a nonreplaced element, you must declare a width for that element. Otherwise, according to the CSS specification, the element’s width will tend toward zero. Thus, a floated paragraph could literally be one character wide, assuming one character is the browser’s minimum value for width. If you fail to declare a width value for your floated elements, you could end up with something like Figure 10-4. (It’s unlikely, granted, but still possible.)

如果浮动的是非置换元素,必须要为元素设置宽度。否则,根据CSS规范,元素的宽度将趋近于零。因此,浮动的段落可能只有一个字符宽,如果一个字符是浏览器最小width值的话。假如你没有为浮动元素声明width值,可能会得到如图10-4所示的结果(并不常见,但仍有可能。)

Floated text without an explicit width 没有显式声明宽度的浮动文本

No floating at all 完全不浮动

There is one other value for float besides left and right. float: none is used to prevent an element from floating at all.

除了leftright之外,float属性还可以取一个值。float: none的作用是禁止元素浮动。

This might seem a little silly, since the easiest way to keep an element from floating is to avoid declaring a float, right? Well, first of all, the default value of float is none. In other words, the value has to exist in order for normal, nonfloating behavior to be possible; without it, all elements would float in one way or another.

这么做看起来有点蠢,因为不想浮动元素不声明float属性不就好了?首先,float默认属性值为none。换句话说,为了常规情况下元素不浮动,这个值必须存在。否则,所以元素都会以某种方式浮动。

Second, you might want to override a certain style from an imported stylesheet. Imagine that you’re using a server-wide stylesheet that floats images. On one particular page, you don’t want those images to float. Rather than writing a whole new style-sheet, you could place img {float: none;} in your document’s embedded stylesheet. Beyond this type of circumstance, though, there really isn’t much call to actually use float: none.

其次,你可能希望覆盖导入样式表中的某个样式。想象你正在使用一个网站的样式表是浮动图像的。而在某个页面,你不希望这些图像浮动。这时你不用写一个全新的样式表而只需要在文档内嵌的样式表中声明img {float: none;}。除此之外,使用到float: none的情况并不多。

10.1.2 Floating: The Details 浮动:细节

Before we start digging into details of floating, it’s important to establish the concept of a containing block. A floated element’s containing block is the nearest block-level ancestor element. Therefore, in the following markup, the floated element’s containing block is the paragraph element that contains it:

在深入了解浮动细节之前,建立包含块的概念是很重要的。浮动元素的包含块是最近的块级祖先元素。因此,在接下来的标记中,浮动元素的包含块是所在的段落元素。

<h1>
  Test
</h1>
<p>
  This is paragraph text, but you knew that. Within the content of this
  paragraph is an image that's been floated.
  <img src="testy.gif" style="float: right;" /> The containing block for the
  floated image is the paragraph.
</p>

We’ll return to the concept of containing blocks when we discuss positioning in Chapter 11.

在第十一章讨论定位时我们还会回到包含块概念。

Furthermore, a floated element generates a block box, regardless of the kind of element it is. Thus, if you float a link, even though the element is inline and would ordinarily generate an inline box, it generates a block box when floated. It will be laid out and act as if it was, for example, a div. This is not unlike declaring display: block for the floated element, although it is not necessary to do so.

此外,不管元素是声明类型,浮动后得到的都是块级框。因此,如果浮动的是链接,即使元素自身是行内元素并且一般来讲生成行内框,在浮动的情况下也是生成块级框。浮动元素在布局还是行为上都表现是块级元素,比如说像div元素那样。这与为浮动元素声明display: block如出一辙,不过没必要多此一举而已。

A series of specific rules govern the placement of a floated element, so let’s cover those before digging into applied behavior. These rules are vaguely similar to those that govern the evaluation of margins and widths and have the same initial appearance of common sense. They are as follows:

浮动元素的位置由一系列规则约束,再深入探讨具体的行为之前需要了解这些规则。这些规则与计算外边距和宽度的规则略有相似之处,并且看起来都符合常识,这些规则如下:

  1. 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. This is straightforward enough. The outer-left edge of a left-floated element can only go as far left as the inner-left edge of its containing block. Similarly, the furthest right a right-floated element may go is its containing block’s inner-right edge, as shown in Figure 10-5. (In this and subsequent figures, the circled numbers show the position where the markup element actually appears in relation to the source, and the numbered boxes show the position and size of the floated visible element.)
  1. 浮动元素的左(或右)外边界不能超过包含块的左(或右)内边界。这是显而易见的。左浮动的元素的左外边界最多只能到达包含块的左内边界。类似地,右浮动的元素最多只能到包含块的右内边界,如图10-5所示(下图和后面几个插图中的带圆圈的数字是元素的标记在源码中的实际位置,而带数字的方框表示元素浮动后的位置和尺寸)。

Floating to the left (or right) 左(或右)浮动

  1. The left, outer edge of a floated element must be to the right of the right, outer edge of a left-floating element that occurs earlier in the document source, unless the top of the later element is below the bottom of the earlier element. Similarly, the right, outer edge of a floated element must be to the left of the left, outer edge of a right-floating element that comes earlier in the document source, unless the top of the later element is below the bottom of the earlier element. This rule prevents floated elements from “overwriting” each other. If an element is floated to the left, and another floated element is already there, the latter element will be placed against the outer-right edge of the previously floated element. If, however, a floated element’s top is below the bottom of all earlier floated images, then it can float all the way to the inner-left edge of the parent. Some examples of this are shown in Figure 10-6.
  1. 如果在文档源码中处于前面的元素向左浮动,那后面的浮动元素的左外边界必定在前一个元素右外边界的右侧,除非后一个元素的顶边在前一个元素的底边以下。类似地,如果在文档中处于前面的元素向右浮动,后面的浮动元素的右外边界必定在前一个元素左外边界的左侧,除非后一个元素的顶边在前一个元素的底边以下。这条规则的好处是能够避免浮动的元素相互遮盖。如果向左浮动一个元素,而左侧已经有浮动的元素了,那么后浮动的元素将靠紧前面浮动元素的右外边界。然而,如果浮动元素的顶边在前面浮动元素的底边以下,那么后浮动的元素将直达父元素的左内边界。图10-6中有几个例子。

Keeping floats from overlapping 避免浮动元素重叠

The advantage of this rule is that all your floated content will be visible, since you don’t have to worry about one floated element obscuring another. This makes floating a fairly safe thing to do. The situation is markedly different when using positioning, where it is very easy to cause elements to overwrite one another.

这条规则的好处是,能确保所有浮动的内容可见,因此你不必担心一个浮动元素遮盖另一个浮动元素,这使得浮动可以安心使用。而定位的情况则截然不同,因为定位很容易使得一个元素遮盖另一个元素。

  1. 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 leftfloating element to its left. This rule prevents floated elements from overlapping each other. Let’s say you have a body that is 500 pixels wide, and its sole content is two images that are 300 pixels wide. The first is floated to the left, and the second is floated to the right. This rule prevents the second image from overlapping the first by 100 pixels. Instead, it is forced down until its top is below the bottom of the right-floating image, as depicted in Figure 10-7.
  1. 左浮动元素的右外边界不能在右浮动元素的左外边界的右侧。右浮动元素的左外边界不能再左浮动元素的右外边界的左侧。这条规则避免浮动的元素重叠。假如网页正文的宽度是500像素,内容里有两个图像,都是300像素宽。第一个图像向左浮动,第二个图像向右浮动。根据这条规则,第二个图像不会与第一个图像出现100像素宽的重叠区域。事实上,第二个图像的顶边将正好在左浮动图像的底边以下,如图10-7所示。

More overlap prevention 避免重叠

  1. A floating element’s top may not be higher than the inner top of its parent. If a floating element is between two collapsing margins, then the floated element is placed as though it had a block-level parent element between the two elements. The first part of this rule keeps floating elements from floating all the way to the top of the document. The correct behavior is illustrated in Figure 10-8. The second part of this rule fine-tunes the alignment in some situations—for example, when the middle of three paragraphs is floated. In that case, the floated paragraph is floated as if it had a block-level parent element (say, a div). This prevents the floated paragraph from moving up to the top of whatever common parent the three paragraphs share.
  1. 浮动元素的顶边不能比父元素的内顶边高。如果浮动元素位于两个折叠的外边距之间,在两个元素之间放置它的位置时,将视其有个块级父元素。 这条规则的前半部分避免浮动元素一直向文档的顶端浮动。正确的行为如图 10-8 所示.这条规则的后半部分进一步规定某些情况下的对齐方式。例如,有三个段落,中间那个段落是浮动的。此时,中间那个段落在浮动时好似有一个块级父元素(比如 div )。这样能避免浮动的段落一直向上移动,超出三个段落共有的父元素。

Unlike balloons, floated elements can’t float upward 浮动的元素不像气球那样一直往上飘

  1. A floating element’s top may not be higher than the top of any earlier floating or block-level element. Similarly to rule 4, rule 5 keeps floated elements from floating all the way to the top of their parent elements. It is also impossible for a floated element’s top to be any higher than the top of a floated element that occurs earlier. Figure 10-9 is an example of this: since the second float was forced to be below the first one, the third float’s top is even with the top of the second float, not the first.
  1. 浮动元素的顶边不能比前方任何一个浮动元素或块级元素的顶边高。 与第 4 条规则一样,这条规则避免浮动的元素一直向上移动,超出父元素的顶边。而且,浮动元素的顶边不能比前方任何一个浮动元素的顶边高。图 10-9是个例子:因为第二个浮动元素必须在第一个浮动元素的下方,所以第三个浮动元素的顶边与第二个浮动元素平齐,而不是与第一个浮动元素平齐。

Keeping floats below their predecessors 浮动元素在前方浮动的元素下方

  1. A floating element’s top may not be higher than the top of any line box that contains a box generated by an element that comes earlier in the document source. Similarly to rules 4 and 5, this rule further limits the upward floating of an element by preventing it from being above the top of a line box containing content that precedes the floated element. Let’s say that, right in the middle of a paragraph, there is a floated image. The highest the top of that image may be placed is the top of the line box from which the image originates. As you can see in Figure 10-10, this keeps images from floating too far upward.
  1. 浮动元素的顶边不能高干文档源码中出现在浮动元素之前的元素生成的框体所在的行框的顶边。与第4条和第5条规则类似,这条规则进一步限制浮动元素上移的幅度.以免浮动元素超出前方内容所在的行框顶边。假如一个段落中有个浮动的图像,那么图像顶边能到达的最高位置是图像原来所在的那一行行框的顶边。从图 10-10可以看出,这样能避免图像向上浮动太远。

Keeping floats level with their context 让浮动元素与所在的上下文位于同一高度

  1. A left-floating element that has another floating element to its left may not have its right outer edge to the right of its containing block’s right edge. Similarly, a right-floating element that has another floating element to its right may not have its right outer edge to the left of its containing block’s left edge. In other words, a floating element cannot stick out beyond the edge of its containing element, unless it’s too wide to fit on its own. This prevents a situation where a succeeding number of floated elements could appear in a horizontal line and far exceed the edges of the containing block. Instead, a float that would otherwise stick out of its containing block by appearing next to another one will be floated down to a point below any previous floats, as illustrated by Figure 10-11 (in the figure, the floats start on the next line in order to more clearly illustrate the principle at work here).

左浮动元素的左边如果还有一个向左浮动的元素,那么它的右外边界不能在包含块右边界的右侧。类似地,右浮动元素的右边如果还有一个向右浮动的元素,那么它的右外边界不能在包含块左边界的左侧。 也就是说,浮动元素不可能超出容纳元素的边界,除非浮动元素太宽,包含元素放不下。这样能避免多个连续的浮动元素全部排在一行,超出包含块的边界。如果相邻的浮动元素排在一起,可能超出包含块的话,后面的部分浮动元素将放到前面的浮动元素下面的某个位置,如图10-11所示(为了更好地说明这条规则,图中可能导致超出边界的浮动元素从下一行开始排列)。

If there isn’t room, floats get pushed to a new “line” 空间不够时,浮动元素被挤到新的“一行”

  1. A floating element must be placed as high as possible. Rule 8 is, as you might expect, subject to the restrictions introduced by the previous seven rules. Historically, browsers aligned the top of a floated element with the top of the line box after the one in which the image’s tag appears. Rule 8, however, implies that its top should be even with the top of the same line box as that in which its tag appears, assuming there is enough room. The theoretically correct behaviors are shown in Figure 10-12.

Given the other constraints, go as high as possible

  1. A left-floating element must be put as far to the left as possible, and a rightfloating element as far to the right as possible. A higher position is preferred to one that is further to the right or left. Again, this rule is subject to restrictions introduced in the preceding rules. As you can see in Figure 10-13, it is pretty easy to tell when an element has gone as far as possible to the right or left.

Get as far to the left (or right) as possible

10.1.3 Applied Behavior

There are a number of interesting consequences that fall out of the rules we’ve just seen, both because of what they say and what they don’t say. The first thing to discuss is what happens when the floated element is taller than its parent element. This happens quite often, as a matter of fact. Take the example of a short document, composed of no more than a few paragraphs and h3 elements, where the first paragraph contains a floated image. Further, this floated image has a margin of 5 pixels (5px). You would expect the document to be rendered as shown in Figure 10-14.

Expected floating behavior

Nothing there is unusual, but Figure 10-15 shows what happens when you set the first paragraph to have a background.

There is nothing different about the second example, except for the visible background. As you can see, the floated image sticks out of the bottom of its parent element. It also did so in the first example, but it was less obvious there because you couldn’t see the background. The floating rules we discussed earlier address only the left, right, and top edges of floats and their parents. The deliberate omission of bottom edges requires the behavior in Figure 10-15.

Backgrounds and floated elements

CSS 2.1 clarified one aspect of floated-element behavior, which is that a floated element will expand to contain any floated descendants. (Previous versions of CSS were unclear about what should happen.) Thus, you could contain a float within its parent element by floating the parent, as in this example:

<div style="float: left; width: 100%;">
  <img src="hay.gif" style="float: left;" /> The 'div' will stretch around the
  floated image because the 'div' has been floated.
</div>

On a related note, consider backgrounds and their relationship to floated elements that occur earlier in the document, which is illustrated in Figure 10-16. Because the floated element is both within and outside of the flow, this sort of thing is bound to happen. What’s going on? The content of the heading is being “displaced” by the floated element. However, the heading’s element width is still as wide as its parent element. Therefore, its content area spans the width of the parent, and so does the background. The actual content doesn’t flow all the way across its own content area so that it can avoid being obscured behind the floating element.

Element backgrounds “slide under” floated elements

Negative margins

Interestingly, negative margins can cause floated elements to move outside of their parent elements. This seems to be in direct contradiction to the rules explained earlier, but it isn’t. In the same way that elements can appear to be wider than their parents through negative margins, floated elements can appear to protrude out of their parents.

Let’s consider an image that is floated to the left, and that has left and top margins of -15px. This image is placed inside a div that has no padding, borders, or margins. The result is shown in Figure 10-17.

Floating with negative margins

Contrary to appearances, this does not violate the restrictions on floated elements being placed outside their parent elements.

Here’s the technicality that permits this behavior: a close reading of the rules in the previous section will show that the outer edges of a floating element must be within the element’s parent. However, negative margins can place the floated element’s content such that it effectively overlaps its own outer edge, as detailed in Figure 10-18.

The details of floating up and left with negative margins

The math situation works out something like this: assume the top, inner edge of the div is at the pixel position 100. The browser, in order to figure out where the top, inner edge of the floated element should be, will do this: 100px + (-15px) margin + 0 padding = 85px. Thus, the top, inner edge of the floated element should be at pixel position 85; even though this is higher than the top, inner edge of the float’s parent element, the math works out such that the specification isn’t violated. A similar line of reasoning explains how the left, inner edge of the floated element can be placed to the left of the left, inner edge of its parent.

Many of you may have an overwhelming desire to cry “Foul!” right about now. Personally, I don’t blame you. It seems completely wrong to allow the top, inner edge to be higher than the top, outer edge, for example; but with a negative top margin, that’s exactly what you get—just as negative margins on normal, nonfloated elements can make them visually wider than their parents. The same is true on all four sides of a floated element’s box: set the margins to be negative, and the content will overrun the outer edge without technically violating the specification.

There is one important question here: what happens to the document display when an element is floated out of its parent element by using negative margins? For example, an image could be floated so far up that it intrudes into a paragraph that has already been displayed by the user agent. In such a case, it’s up to the user agent to decide whether the document should be reflowed.

The CSS specification explicitly states that user agents are not required to reflow previous content to accommodate things that happen later in the document. In other words, if an image is floated up into a previous paragraph, it may overwrite whatever was already there. On the other hand, the user agent may handle the situation by flowing content around the float. Either way, it’s probably a bad idea to count on a particular behavior, which makes the utility of negative margins on floats somewhat limited. Hanging floats are probably fairly safe, but trying to push an element upward on the page is generally a bad idea.

There is one other way for a floated element to exceed its parent’s inner left and right edges, and that’s when the floated element is wider than its parent. In that case, the floated element will overflow the right or left inner edge—depending on which way the element is floated—in its best attempt to display itself correctly. This will lead to a result like that shown in Figure 10-19.

Floating an element that is wider than its parent

10.1.4 Floats, Content, and Overlapping

An even more interesting question is this: what happens when a float overlaps content in the normal flow? This can happen if, for example, a float has a negative margin on the side where content is flowing past (e.g., a negative left margin on a rightfloating element). You’ve already seen what happens to the borders and backgrounds of block-level elements. What about inline elements?

CSS1 and CSS2 were not completely clear about the expected behavior in such cases. CSS 2.1 clarified the subject with explicit rules. These state that:

  • An inline box that overlaps with a float has its borders, background, and content all rendered “on top” of the float.
  • A block box that overlaps with a float has its borders and background rendered “behind” the float, whereas its content is rendered “on top” of the float. To illustrate these rules, consider the following situation:
<img src="testy.gif" class="sideline" />
<p class="box">
  This paragraph, unremarkable in most ways, does contain an inline element.
  This inline contains some
  <strong
    >strongly emphasized text, which is so marked to make an important
    point</strong
  >. The rest of the element's content is normal anonymous inline content.
</p>
<p>
  This is a second paragraph. There's nothing remarkable about it, really.
  Please move along to the next bit.
</p>
<h2 id="jump-up">
  A Heading!
</h2>

To that markup, apply the following styles, with the result seen in Figure 10-20:

.sideline {
  float: left;
  margin: 10px -15px 10px 10px;
}
p.box {
  border: 1px solid gray;
  background: hsl(117, 50%, 80%);
  padding: 0.5em;
}
p.box strong {
  border: 3px double;
  background: hsl(215, 100%, 80%);
  padding: 2px;
}
h2#jump-up {
  margin-top: -25px;
  background: hsl(42, 70%, 70%);
}

Layout behavior when overlapping floats

The inline element (strong) completely overlaps the floated image—background, border, content, and all. The block elements, on the other hand, have only their content appear on top of the float. Their backgrounds and borders are placed behind the float. The described overlapping behavior is independent of the document source order. It does not matter if an element comes before or after a float: the same behaviors still apply.

10.2 Clearing

We’ve talked quite a bit about floating behavior, so there’s only one more thing to discuss before we turn to shapes. You won’t always want your content to flow past a floated element—in some cases, you’ll specifically want to prevent it. If you have a document that is grouped into sections, you might not want the floated elements from one section hanging down into the next. In that case, you’d want to set the first element of each section to prohibit floating elements from appearing next to it. If the first element might otherwise be placed next to a floated element, it will be pushed down until it appears below the floated image, and all subsequent content will appear after that, as shown in Figure 10-21.

Displaying an element in the clear

This is done with clear.

For example, to make sure all h3 elements are not placed to the right of left-floating elements, you would declare h3 {clear: left;}. This can be translated as “make sure that the left side of an h3 is clear of floating images,” and has an effect very similar to the HTML construct <br clear="left">. (Ironically, browsers’ default behavior is to have br elements generate inline boxes, so clear doesn’t apply to them unless you change their display!) The following rule uses clear to prevent h3 elements from flowing past floated elements to the left side:

h3 {
  clear: left;
}

While this will push the h3 past any left-floating elements, it will allow floated elements to appear on the right side of h3 elements, as shown in Figure 10-22.

Clear to the left, but not the right

In order to avoid this sort of thing, and to make sure that h3 elements do not coexist on a line with any floated elements, you use the value both:

h3 {
  clear: both;
}

Understandably enough, this value prevents coexistence with floated elements on both sides of the cleared element, as demonstrated in Figure 10-23.

Clear on both sides

If, on the other hand, we were only worried about h3 elements being pushed down past floated elements to their right, then you’d use h3 {clear: right;}.

Finally, there’s clear: none, which allows elements to float to either side of an element. As with float: none, this value mostly exists to allow for normal document behavior, in which elements will permit floated elements to both sides. none can be used to override other styles, as shown in Figure 10-24. Despite the document-wide rule that h3 elements will not permit floated elements to either side, one h3 in particular has been set so that it does permit floated elements on either side:

h3 {
  clear: both;
}
<h3 style="clear: none;">What's With All The Latin?</h3>

Not clear at all

In CSS1 and CSS2, clear worked by increasing the top margin of an element so that it ended up below a floated element, so any margin width set for the top of a cleared element was effectively ignored. That is, instead of being 1.5em, for example, it would be increased to 10em, or 25px, or 7.133in, or however much was needed to move the element down far enough so that the content area is below the bottom edge of a floated element.

In CSS 2.1, clearance was introduced. Clearance is extra spacing added above an element’s top margin in order to push it past any floated elements. This means that the top margin of a cleared element does not change when an element is cleared. Its downward movement is caused by the clearance instead. Pay close attention to the placement of the heading’s border in Figure 10-25, which results from the following:

img.sider {
  float: left;
  margin: 0;
}
h3 {
  border: 1px solid gray;
  clear: left;
  margin-top: 15px;
}
<img src="chrome.jpg" class="sider" height="50" width="50" />
<img src="stripe.gif" height="10" width="100" />
<h3>
  Why Doubt Salmon?
</h3>

Clearing and its e�ect on margins

There is no separation between the top border of the h3 and the bottom border of the floated image because 25 pixels of clearance were added above the 15-pixel top margin in order to push the h3’s top border edge just past the bottom edge of the float. This will be the case unless the h3’s top margin calculates to 40 pixels or more, in which case the h3 will naturally place itself below the float, and the clear value will be irrelevant.

In most cases, you can’t know how far an element needs to be cleared. The way to make sure a cleared element has some space between its top and the bottom of a float is to put a bottom margin on the float itself. Therefore, if you want there to be at least 15 pixels of space below the float in the previous example, you would change the CSS like this:

img.sider {
  float: left;
  margin: 0 0 15px;
}
h3 {
  border: 1px solid gray;
  clear: left;
}

The floated element’s bottom margin increases the size of the float box, and thus the point past which cleared elements must be pushed. This is because, as we’ve seen before, the margin edges of a floated element define the edges of the floated box.

10.3 Float Shapes

Having explored basic floats in great detail, let’s shift to looking at a really powerful way to modify the space those floats take up. The CSS Shapes module, a recent addition to the specification, describes a small set of properties that allow you to reshape the float box in nonrectangular ways. Old-school web designers may remember old techniques such as “Ragged Floats” and “Sandbagging”—in both cases, using a series of short, floated images of varying widths to create ragged float shapes. Thanks to CSS Shapes, these tricks are no longer needed.

In the future, Shapes may be available for nonfloated elements, but as of late 2017, they’re only allowed on floated elements.

10.3.1 Creating a Shape

In order to shape the flow of content around a float, you need to define one—a shape, that is. The property shape-outside is how you do so.

With none, there’s no shaping except the margin box of the float itself—same as it ever was. That’s straightforward and boring. Time for the good stuff.

Let’s start with using an image to define the float shape, as it’s both the simplest and (in many ways) the most exciting. Say we have an image of a crescent moon, and we want the content to flow around the visible parts of it. If that image has transparent parts, as in a GIF87a or a PNG, then the content will flow into those transparent parts, as shown in Figure 10-26:

img.lunar {float: left; shape-outside: url(moon.png);}
<img class="lunar" src="moon.png">

We’ll talk in the following sections about how to push the content away from the visible parts of the image, and how to vary the transparency threshold that determines the shape; but for now, let’s just savor the power this affords us.

Using an image to define a float shape

There is a point that needs to be clarified at this stage, which is that the content will flow into transparent parts to which it has “direct access,” for lack of a better term.

That is, the content doesn’t flow to both the left and right of the image in Figure 10-26, but just the right side. That’s the side that faces the content, it being a left-floated image. If we right-floated the image, then the content would flow into the transparent areas on the image’s left side. This is illustrated in Figure 10-27 (with the text right-aligned to make the effect more obvious):

p {
  text-align: right;
}
img.lunar {
  float: right;
  shape-outside: url(moon.png);
}

An image float shape on the right

Always remember that the image has to have actual areas of transparency to create a shape. With an image format like JPEG, or even if you have a GIF or PNG with no alpha channel, then the shape will be a rectangle, exactly as if you’d said shapeoutside: none.

Now let’s turn to the <basic-shape> and <shape-box> values. A basic shape is one of the following types:

  • inset()
  • circle()
  • ellipse()
  • polygon()

In addition, the <shape-box> can be one of these types:

  • margin-box
  • border-box
  • padding-box
  • content-box

These shape boxes indicate the outermost limits of the shape. You can use them on their own, as illustrated in Figure 10-28.

The basic shape boxes

The default is the margin box, which makes sense, since that’s what float boxes use when they aren’t being shaped. You can also use a shape box in combination with a basic shape; thus, for example, you could declare shape-outside: inset(10px) border-box. The syntax for each of the basic shapes is different, so we’ll take them in turn.

Inset shapes

If you’re used to working with border images, or even the old clip property, inset shapes should seem familiar. Even if you aren’t, the syntax isn’t too complicated. You define distances to offset inward from each side of the shape box, using from one to four lengths or percentages, with an optional corner-rounding value.

To pick a simple case, suppose we just want to shrink the shape 2.5em inside the shape box:

shape-outside: inset(2.5em);

Four offsets are created, each 2.5 em inward from the outside edge of the shape box. In this case, the shape box is the margin box, since we haven’t altered it. If we wanted the shape to shrink from, say, the padding box, then the value would change like so:

shape-outside: inset(2.5em) padding-box;

See Figure 10-29 for illustrations of the two inset shapes we just defined.

Insets from two basic shape boxes

As with margins, padding, borders, and so on, value replication is in force: if there are fewer than four lengths or percentages, then the missing values are derived from the given values. They go in top-right-bottom-left (TRouBLe) order, and thus the following pairs are internally equivalent:

shape-outside: inset(23%);
shape-outside: inset(23% 23% 23% 23%); /* same as previous */
shape-outside: inset(1em 13%);
shape-outside: inset(1em 13% 1em 13%); /* same as previous */
shape-outside: inset(10px 0.5em 15px);
shape-outside: inset(10px 0.5em 15px 0.5em); /* same as previous */

An interesting addition to inset shapes is the ability to round the corners of the shape once the inset has been calculated. The syntax (and effects) are identical to the border-radius property. Thus, if you wanted to round the corners of the float shape with a 5-pixel round, you’d write something like:

shape-outside: inset(7%) round 5px;

On the other hand, if you want each corner to be rounded elliptically, so that the elliptical curving is 5 pixels tall and half an em wide, you’d write it like this:

shape-outside: inset(7% round 0.5em/5px);

Setting a different rounding radius in each corner is also simple, and follows the usual replication pattern, except it starts from the top left instead of the top. So if you have more than one value, they’re in the order TL-TR-BR-BL (TiLTeR-BuRBLe), and are filled in by copying declared values in for the missing values. You can see a few examples of this in Figure 10-30. (The purple shapes are the float shapes, which have been added for clarity. Browsers do not actually draw the float shapes on the page.)

Rounding the corners of a shape box

Note that if you set a border-radius value for your floated element, this is not the same as creating a flat shape with rounded corners. Remember that shape-outside defaults to none, so the floated element’s box won’t be affected by the rounding of borders. If you want to have text flow closely past the border rounding you’ve defined with border-radius, you’ll need to supply identical rounding values to shape-outside.

Circles and ellipses

Circular and elliptical float shapes use very similar syntax, which makes sense. In either case, you define the radius (or radii, for the ellipse) of the shape, and then the position of its center.

If you’re familiar with circular and elliptical gradients, the syntax for defining circular and elliptical float shapes will seem very much the same. There are some important caveats, however, as this section will explore.

Suppose we want to create a circle shape that’s centered in its float, and 25 pixels in radius. We can accomplish that in any of the following ways:

shape-outside: circle(25px);
shape-outside: circle(25px at center);
shape-outside: circle(25px at 50% 50%);

Regardless of which we use, the result will be that shown in Figure 10-31.

A circular float shape

Something to watch out for is that shapes cannot exceed their shape box, even if you set up a condition where that seems possible. For example, suppose we applied the previous 25-pixel-radius rule to a small image, one that’s no more than 30 pixels on a side. In that case, you’ll have a circle 50 pixels in diameter centered on a rectangle that’s smaller than the circle. What happens? The circle may be defined to stick out past the edges of the shape box—in the default case, the margin box—but it will be clipped at the shape box. Thus, given the following rules, the content will flow past the image as if it had no shape, as shown in Figure 10-32:

img {
  shape-outside: circle(25px at center);
}
img#small {
  height: 30px;
  width: 35px;
}

A rather small circular float shape for an even smaller image

We can see the circle extending past the edges of the image in Figure 10-32, but notice how the text flows along the edge of the image, not the float shape. Again, that’s because the actual float shape is clipped by the shape box; in Figure 10-32, that’s the margin box, which is at the outer edge of the image. So the actual float shape isn’t a circle, but a box the exact dimensions of the image.

The same holds true no matter what edge you define to be the shape box. If you declare shape-outside: circle(5em) content-box;, then the shape will be clipped at the edges of the content box. Content will be able to flow over the padding, borders, and margins, and will not be pushed away in a circular fashion.

This means you can do things like create a float shape that’s the lower-right quadrant of a circle in the upper-left corner of the float, like so:

shape-outside: circle(3em at top left);

For that matter, if you have a perfectly square float, you can define a circle-quadrant that just touches the opposite sides, using a percentage radius:

shape-outside: circle(50% at top left);

But note: that only works if the float is square. If it’s rectangular, oddities creep in. Take this example, which is illustrated in Figure 10-33:

img {
  shape-outside: circle(50% at center);
}
img#tall {
  height: 150px;
  width: 70px;
}

The circular float shape that results from a rectangle

Don’t bother trying to pick which dimension is controlling the 50% calculation, because neither is. Or, in a sense, both are.

When you define a percentage for the radius of a circular float shape, it’s calculated with respect to a calculated reference box. The height and width of this box are calculated as follows:

In effect, this creates a square that’s a blending of the float’s intrinsic height and width. In the case of our floated image that’s 70 × 150 pixels, that works out to a square that’s 117.047 pixels on a side. Thus, the circle’s radius is 50% of that, or 58.5235 pixels. Once again, note how the content in Figure 10-34 is flowing past the image and ignoring the circle. That’s because the actual float shape is clipped by the shape box, so the final float shape would be a kind of vertical bar with rounded ends, something very much like what’s shown in Figure 10-34.

A clipped float shape

It’s a lot simpler to position the center of the circle and have it grow until it touches either the closest side to the circle’s center, or the farthest side from the circle’s center. Both are easily possible, as shown here and illustrated in Figure 10-35:

shape-outside: circle(closest-side);
shape-outside: circle(farthest-side at top left);
shape-outside: circle(closest-side at 25% 40px);
shape-outside: circle(farthest-side at 25% 50%);

Various circular float shapes

In one of the examples in Figure 10-35, the shape was clipped to its shape box, whereas in the others, the shape was allowed to extend beyond it. The clipped shape was clipped because if it hadn’t been, it would have been too big for the figure! We’ll see this again in an upcoming figure.

Now, how about ellipses? Besides using the name ellipse(), the only syntactical difference between circles and ellipses is that you define two radii instead of one radius. The first is the x (horizontal) radius, and the second is the y (vertical) radius. Thus, for an ellipse with an x radius of 20 pixels and a y radius of 30 pixels, you’d declare ellipse(20px 30px). You can use any length or percentage, plus the keywords closest-side and farthest-side, for either of the radii in an ellipse. A number of possibilities are shown in Figure 10-36.

Defining float shapes with ellipses

As of late 2017, there were inconsistencies with Chrome’s handling of farthest-side when applied to ellipses. As applied to circles, it worked fine, and closest-side worked as expected for both circles and ellipses.

With regards to percentages, things are a little different with ellipses than they are with circles. Instead of a calculated reference box, percentages in ellipses are calculated against the axis of the radius. Thus, horizontal percentages are calculated with respect to the width of the shape box, and vertical percentages with respect to the height. This is illustrated in Figure 10-37.

Elliptical float shapes and percentages

As with any basic shape, an elliptical shape is clipped at the edges of the shape box.

Polygons

Polygons are a lot more complicated to write, though they’re probably a little bit easier to understand. You define a polygonal shape by specifying a comma-separated list of x-y coordinates, expressed as either lengths or percentages, calculated from the top left of the shape box. Each x-y pair is a vertex in the polygon. If the first and last vertices are not the same, the browser will close the polygon by connecting them. (All polygonal float shapes must be closed.)

So let’s say we want a diamond shape that’s 50 pixels tall and wide. If we start from the top vertex, the polygon() value would look like this:

polygon(25px 0, 50px 25px, 25px 50px, 0 25px)

Percentages have the same behavior as they do in background-image positioning (for example), so we can define a diamond shape that always “fills out” the shape box, it would be written like so:

polygon(50% 0, 100% 50%, 50% 100%, 0 50%)

The result of this and the previous polygon example are shown in Figure 10-38.

A polygonal float shape

Those examples started from the top because that’s the habit in CSS, but they didn’t have to. All of the following will yield the same result:

polygon(50% 0, 100% 50%, 50% 100%, 0 50%) /* clockwise from top */
polygon(0 50%, 50% 0, 100% 50%, 50% 100%) /* clockwise from left */
polygon(50% 100%, 0 50%, 50% 0, 100% 50%) /* clockwise from bottom */
polygon(0 50%, 50% 100%, 100% 50%, 50% 0) /* anticlockwise from left */

As before, remember: a shape can never exceed the shape box, but is always clipped to it. So even if you create a polygon with coordinates that lie outside the shape box (by default, the margin box), the polygon will get clipped. This is demonstrated in Figure 10-39.

How a float shape is clipped when it exceeds the shape box

There’s an extra wrinkle to polygons, which is that you can toggle their fill rule. By default, the fill rule is nonzero, but the other possible value is evenodd. It’s easier to show the difference than to describe it, so here’s a star polygon with two different fill rules, illustrated in Figure 10-40:

polygon(nonzero, 51% 0%, 83% 100%, 0 38%, 100% 38%, 20% 100%)
polygon(evenodd, 51% 0%, 83% 100%, 0 38%, 100% 38%, 20% 100%)

The two polygonal fills

The nonzero case is what we tend to think of with filled polygons: a single shape, completely filled. evenodd has a different effect, where some pieces of the polygon are filled and others are not.

This particular example doesn’t show much difference, since the part of the polygon that’s missing is completely enclosed by filled parts, so the end result is the same either way. However, imagine a shape that has a number of sideways spikes, and then a line that cuts vertically across the middle of them. Rather than a comb shape, you’d end up with a set of discontinuous triangles. There are a lot of possibilities.

As of late 2017, the one browser that supports CSS Shapes, Chrome, does not support fill styles. All polygons are treated as nonzero.

As you can imagine, a polygon can become very complex, with a large number of vertices. You’re welcome to work out the coordinates of each vertex on paper and type them in, but it makes a lot more sense to use a tool to do this. A good example of such a tool is the Shapes Editor available for Chrome. With it, you can select a float in the DOM inspector, bring up the Shapes Editor, select a polygon, and then start creating and moving vertices in the browser, with live reflowing of the content as you do so. Then, once you’re satisfied, you can drag-select-copy the polygon value for pasting into your stylesheet. Figure 10-41 shows a screenshot of the Shapes Editor in action.

The Chrome Shapes Editor in action

Due to Cross-Origin Resource Sharing (CORS) restrictions, shapes annot be edited with the Shapes Editor unless they’re being loaded over HTTP(S) from the same origin server as the HTML and CSS. Loading local files from your HDD/SSD will prevent the shapes from being editable. The same restriction prevents shapes from being loaded off local storage via the url() mechanism.

10.3.2 Shaping with Image Transparency

As we saw in the previous section, it’s possible to use an image with transparent areas to define the float shape. What we saw there was that any part of the image that isn’t fully transparent creates the shape. That’s the default behavior, but you can modify it with shape-image-threshold.

This property lets you decide what level of transparency determines an area where content can flow; or, conversely, what level of opacity defines the float shape. Thus, with shape-image-threshold: 0.5, any part of the image with more than 50% transparency can allow content to flow into it, and any part of the image with less than 50% transparency is part of the float shape. This is illustrated in Figure 10-42.

Using image opacity to define the float shape at the 50% opacity level

If you set the value of the shape-image-threshold property to 1.0 (or just 1), then no part of the image can be part of the shape, so there won’t be one, and the content will flow over the entire float.

On the other hand, a value of 0.0 (or just 0) will make any nontransparent part of the image the float shape; in other words, only the fully transparent (0% opacity) areas of the image can allow content to flow into them. Furthermore, any value below zero is reset to 0.0, and any above one is reset to 1.0.

10.3.3 Adding a Shape Margin

Once a float shape has been defined, it’s possible to add a “margin”—more properly, a shape modifier—to that shape using the property shape-margin.

Much like a regular element margin, a shape margin pushes content away by either a length or a percentage; a percentage is calculated with respect to the width of the element’s containing block, just as are regular margins.

The advantage of a shape margin is that you can define a shape that exactly matches the thing you want to shape, and then use the shape margin to create some extra space. Take an image-based shape, where part of the image is visible and the rest is transparent. Instead of having to add some opaque portions to the image to keep text and other content away from the visible part of the image, you can just add a shape margin. This enlarges the shape by the distance supplied.

In detail, the new shape is found by drawing a line perpendicular from each point along the basic shape, with a length equal to the value of shape-margin, to find a point in the new shape. At sharp corners, a circle is drawn centered on that point with a radius equal to the value of shape-margin. After all that, the new shape is the smallest shape that can describe all those points and circles (if any).

Remember, though, that a shape can never exceed the shape box. Thus, by default, the shape can’t get any bigger than the margin box of the un-shaped float. Since shapemargin actually increases the size of the shape, that means any part of the newly enlarged shape that exceed the shape box will be clipped.

To see what this means, consider the following, as illustrated in Figure 10-43:

img {float: left; margin: 0; shape-outside: url(star.svg);
 border: 1px solid hsla(0,100%,50%,0.25);}
#one {shape-margin: 0;}
#two {shape-margin: 1.5em;}
#thr (shape-margin: 10%;}

Adding margins to float shapes

Notice the way the content flows past the second and third examples. There are definitely places where the content gets closer than the specified shape-margin, because the shape has been clipped at the margin box. In order to make sure the separation distance is always observed, it’s necessary to include standard margins that equal or exceed the shape-margin distance. For example, we could have avoided the problem by modifying two of the rules like so:

#two {shape-margin: 1.5em; margin: 0 1.5em 1.5em 0;}
#thr (shape-margin: 10%; margin: 0 10% 10% 0;}

In both cases, the right and bottom margins are set to be the same as the shapemargin value, ensuring that the enlarged shape will never exceed the shape box on those sides. This is demonstrated in Figure 10-44.

Making sure the shape margins don’t get clipped

If you have a float go to the right, then you’ll have to adjust its margins to create space below and to the left, not the right, but the principle is the same.

10.4 Summary

Floats may be a fundamentally simple aspect of CSS, but that doesn’t keep them from being useful and powerful. They fill a vital and honorable niche, allowing the placement of content to one side while the rest of the content flows around it. And thanks to float shapes, we’re not limited to square float boxes any more.