Chrome Quarters: a lightweight alternative to sliding doors.

In an ideal world, HTML would mark up a document in a completely semantic way, with no extra nesting or non-semantic elements. There’s a very big stigma against use of “chrome-only” chunks of markup. I’d like to make the case that sometimes, such markup is, if not ideal, at least better than more popular alternatives.

Say you wanted a dynamically sized box to have nice borders with beveled or rounded 3D corners. Here’s the lightweight markup you start with (39 chars without content):

<div id="mybox">
  <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
 doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
 veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>
  <p>Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit.</p>
</div>

You want to have that look something like this:

Box with rounded corners, beveling and a gradient

Box with rounded corners, beveling and a gradient

Unfortunately, CSS doesn’t give us any way to do that without specifying an exact height and width for the box — a major accessibility block. We need a nested container and a fake header (I’ll use the Yahoo! standard container model) in order to use the sliding doors technique in 147 chars without content:

<div id="mybox">
  <div class="hd"><h4></h4></div>
  <div class="bd">
    <div class="wrap">
      <div class="wrap">
        <p>...</p>
        <p>...</p>
      </div>
    </div>
  </div>
</div>

Now we can reach that style (with an appropriately large image). I won’t go into details on how to make CSS sliding doors, as that’s well documented elsewhere. Here’s what the CSS for that might look like:

#mybox {
  max-width: 250px;
}
#mybox .hd, #mybox .hd h4, #mybox .bd, #mybox .bd .wrap {
  background: url(roundedCornerBoxBg1.png) no-repeat;
}
#mybox .hd {
  padding-left: 10px;
  margin-right: 10px;
}
#mybox .hd h4 {
  background-position: top right;
  height: 31px;
  margin: 0 -10px 0 0;
}
#mybox .bd {
  background-position: bottom left;
  padding-left: 10px;
  margin-right: 10px;
}
#mybox .bd .wrap {
  background-position: bottom right;
  margin-right: -10px;
}
#mybox .bd .wrap .wrap {
  background: none;
  position: relative;
  top: -24px;
  padding: 1px 0 0 1px;
  margin-right: 0;
}

There are two markup problems with this: one, we’ve created non-semantic markup that looks semantic, and we’ve had to add three layers of nesting! How is this better than tables?

Even with all that, due to the 31px height that the chrome image has for the top gradient, we have to resort to negative margins for the innermost content wrapper to achieve centering of the content. Our content formatting CSS and chrome CSS are heavily intertwined. Even with that, we still have a large amount of padding on top and bottom that cannot be eliminated.

Nested markup rendered - lots of padding at top and bottom

Nested markup rendered - lots of padding at top and bottom

Another way: Chrome Quarters

Consider this markup instead (142 characters without content, and chrome whitespace removed):

<div id="mybox">
  <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore
veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>
  <p>Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit.</p>
  <b class="chr4c chrome">
    <b class="ul"></b>
    <b class="ur"></b>
    <b class="lr"></b>
    <b class="ll"></b>
  </b>
</div>

Here, we’ve got a separate heirarchy just to hold the chrome, clearly marked as such. The chrome and content markup don’t interfere with one another. Here’s the CSS:

.chr4c {
  display: none;
}
.chr4c * {
  display: block;
  position: absolute;
}
.chr4c .ul {
  background-position: left top;
  top: 0;
  right: 50%;
  bottom: 50%;
  left: 0;
}
.chr4c .ur {
  background-position: right top;
  top: 0;
  right: 0;
  bottom: 50%;
  left: 49%;
}
.chr4c .lr {
  background-position: right bottom;
  top: 49%;
  right: 0;
  bottom: 0;
  left: 49%;
}
.chr4c .ll {
  background-position: left bottom;
  top: 49%;
  right: 50%;
  bottom: 0;
  left: 0;
}
#mybox {
  max-width: 230px;
  min-height: 62px;
  position: relative;
  padding: 1px 10px;
}
#mybox .chr4c {
  display: block;
}
#mybox .chr4c * {
  z-index: -1;
  background-image: url(roundedCornerBoxBg1.png);
}
@media audio, speech {
  .chrome {
    display: none !important;
  }
}

Here, our content formatting CSS is limited to a single rule, almost entirely separate from the chrome styling. A slate of generic rules sets up most of the styling for re-use in many rounded-corner type situations. The four corner pieces are positioned absolutely to occupy 1/4 of the box each (a small amount of overlap allows for IEEE floating point math oddities). The main container is set to position:relative to constrain the absolutely positioned elements.

Finished box with chrome divided into four more-or-less equal quarters of the container.

Finished box with chrome divided into four more-or-less equal quarters of the container. Borders drawn for clarity.

The last rule hides all chrome-only elements from audio browsers, just to be on the safe side. They generally wouldn’t report empty spans, but this assures as much. The b tag is used for brevity, and shouldn’t cause any more trouble than a span.

Browser bugs

The one caveat I must offer: browsers sometimes have some odd rendering differences when it comes to the use of positioning within certain elements. divs will generally work flawlessly, but you’ll find that browser differences abound when it comes to positioning inside buttons and lis. In those cases, know that you’ll likely need to deliver different styles to various browsers.

Despite those limitations, I think you’ll agree that our finished markup and CSS is far more readable, maintainable and reusable than the first. Thoughts?