Skip to content
All Posts

CSS Notes and Tips

Notes on floats, BFCs, table-cell layouts, vertical alignment, and hover borders in CSS.

At work, apart from Django and a few simple scripts, I spent most of my time writing CSS and jQuery. Looking back at some of that code now, it was ugly and inefficient, and the CSS in particular had plenty of room for improvement.

As most people know, HTML and CSS are not hard to learn or use. Their learning curve really is gentle—easier than Vim, even. Writing them well is another matter. Good CSS uses as little code as possible, is easy to change, and performs well. Take a simple case: a div needs a new width and height while its children should still adapt to it. If the children have fixed dimensions, the change is painful. It is usually better to make them adapt to the parent whenever the requirements allow, so only the parent needs changing. As for performance, if CSS can do it, do not use JavaScript. For layout and animations alike, native CSS is fast and broadly compatible.

Clearing floats

Clearing floats is a common problem. A lot of people add an empty div with clear: both, but an after pseudo-element is enough to clear the float at the end of an element automatically:

.clear-fix { overflow: hidden; zoom: 1; }
.clear-fix:after { display: table; content: ""; width: 0; clear: both; }

Putting divs on the same row

The first idea is usually to give every div on the row display: inline-block. The problem is that this leaves a gap between the divs. Its size is usually determined by font-size; setting font-size to 0 removes it. Comments can also be used to remove the gap:

<div class="item"></div><!--
--><div class="item"></div>

Using floats is naturally the better option:

.item { float: left; }

Using BFCs flexibly

BFC (Block Formatting Context) is, literally, a block formatting context. An HTML element creates one when any of the following is true:

  • float is not none
  • overflow is not visible
  • display is table-cell, table-caption, or inline-block
  • position is neither relative nor static

A BFC provides an environment whose elements do not affect the layout of other environments. For example, a floated element creates a BFC, and its children are mainly affected by that float; two floated elements do not affect each other. A BFC has a scope. You can think of it as an independent container whose layout has nothing to do with elements outside it.

A BFC element cannot overlap a float. When the container has enough remaining space for the BFC’s width, browsers place the BFC in the space left on the float’s line.

The source image is no longer available: original image URL

In the image, the description on the left can be any length, and so can its width. The line fills the remaining space on the same row. This is exactly the situation described above for a BFC next to a float:

.desc { float: left; }
.line { overflow: hidden; }
<div>
<div class="desc">Prime time</div>
<div class="line"></div>
</div>

Unexpected vertical alignment

When several divs with class item on the same row use display: inline-block or float: left, adding text or other content inside one of them can make the boxes stop aligning vertically. Oddly, the inner element does not overflow or stretch its parent. I do not fully understand why this happens either, although the fix is simple. If you know the reason, leave a comment:

.item { vertical-align: top; }

Using display: table-cell

Other CSS properties can break table-cell, including float and position: absolute, so it is best not to use display: table-cell together with either of them. Elements with this property are sensitive to width and height, respond to padding, and do not respond to margin.

Vertical centering

.content {
display: table-cell;
border: 1px solid #eee;
width: 600px;
text-align: center;
}
<div class="content">
<p>what a beautiful day</p>
</div>

A two-column adaptive layout

This works when one column has an unknown width—for example, an image whose size is not known—and the other should adjust to fill the remaining space.

.box {
width: 70%;
}
.content {
display: table-cell;
border: 1px solid #eee;
}
.fix {
float: left;
color: #a8c;
}
<div class="box">
<div class="fix">This is left fixed block</div>
<div class="content">
The wind has stilled; the fragrant dust and flowers are gone. At dusk I am too tired to comb my hair. Things remain, but people have changed, and there is nothing to say. When I try, tears come first. I hear that Twin Streams are lovely in spring, and I might take a small boat there. I only fear that even a light boat could not carry so much sorrow.
</div>
</div>

A vertical-alignment trick

Earlier I mentioned using table-cell to center an element vertically inside a div, but that has restrictions on the div’s float and position properties. Here is another way to center content vertically inside a floated container. I found it at work after float made the first approach unusable.

Use an inline-block helper element—a span here—and set both it and the element to be centered to vertical-align: middle:

<div class="frame">
<span class="helper">
<img src="http://jsfiddle.net/img/logo.png" />
</div>
.frame {
float: left;
height: 220px;
width: 300px;
}
.helper {
display: inline-block;
height: 100%;
vertical-align: middle;
}
img {
vertical-align: middle;
max-height: 25px;
max-width: 160px;
}

The idea is that two adjacent inline-block elements can align with each other, so an invisible span with 100% height can help align the image. Source: How to vertically align an image inside a div.

There is still a problem. Suppose the image is followed by a descriptive div block (inline-block), like this:

<div class="frame">
<span class="helper">
<img src="http://jsfiddle.net/img/logo.png" />
<div class="desc">ABCD</div>
</div>

If the requirement is .desc { margin-top: 10px; }, setting it directly also moves the previously centered img down by 10px. The reason is that a block element’s default vertical-align is baseline. Setting margin-top lowers the whole baseline, so the already centered element is affected as well. Set div.desc to another alignment instead; vertical-align: top is a good choice.

Another solution fixes both this problem and the fact that margin may not work on an inline-block block element:

.desc {
postion: relative;
top: 10px;
}

Showing borders on hover

Option 1

For a block element such as a div, set border-width first and set border-color to #fff or transparent. On hover, change only the border color.

Option 2

Set the width and height and use box-sizing: box. Then set the border on hover; its width is included in the element’s width or height.


Originally published on SegmentFault.