Camen Design

c share + remix

Under the Hood #7: Floaty Abbreviations!

Screenshot of floaty abbreviation description effect

That’s a single abbr element (on mouseover).
No spans, divs or superfluous elements. No classes. No Javascript. Just CSS.

Unfortunately the cost of this simplicity is that it only works in Firefox 3.1+, Safari 3, 4 / Chrome 1, 2. If you have any of these, then try it out on this article, which has a number of abbreviations.

I suspect that I am opening a can-of-worms with this one, as the abuse of the abbreviation element is already bad enough. This will only encourage people to do worse. However, it is an effect that I’ve long wanted to do, and has only recently become possible for my target-market with the beta builds of Firefox 3.15

The crux of this is the ability to absolutely position CSS-generated content (i.e. :before & :after). This has been supported in Safari for some time. The bug for Firefox was filed in 2004 (That’s Firefox 0.8). That annoys me beyond description that something that’s part of the CSS2 spec could be left that long, and only now—in 2009—can I actually do the effect (in my favourite browser) I’ve been wanting to do all this time.

In essence it’s very simple. Just use :before to write the abbreviation’s title before the abbreviation and position / style accordingly. I’ve tried on and off over the last few years to do this in Firefox, but it’s simply not possible without absolute positioning. I came up with some clever hacks, but they all fell down in one major way: they were simply not absolutely positioned, so therefore would not hang over any bounding box (for long titles at the page edge).

Screenshot of floaty abbreviation description effect overhanging the side of the page

A second problem is being able to target Firefox 3.5 and not Firefox 3, but also include Safari. Firefox 3 does not include support for the “nth-child” CSS selector, whereas Firefox 3.5 (and Safari) does. This works out handily, as browser support for this selector also matches browser support for absolute positioning of generated content!

Therefore the following selector matches abbreviations with titles, in Firefox 3.5 and Safari. For Firefox 3, no match would be made, and therefore the browser would fall back to the default dotted-underline.

abbr:nth-child(n)[title]:hover { ...

There is however, one problem with this: Opera. It freaks out for some reason and offsets the absolutely-positioned description by the page scroll, as well as other quirks. Opera also doesn’t support border-radius, giving a rather blunt look. I need to somehow exclude Opera entirely, but I don’t know of any CSS selector or hack that can do that. I’ve found ones to exclude Safari specifically, or Opera and Safari, but nothing that can exclude all versions of Opera.

On to the code!

/* the abbreviation itself */
abbr:nth-child(n)[title]:hover {
	/* the abbr element is relative, so that we can absolutely position the `:before` description */
	position: relative;
	/* the negative margin removes the effect of the padding and border so that the floaty does not nudge
	   the surrounding content around */
	padding: 1px 5px; margin: -2px -6px;
	/* use the line-height of the paragraph the abbr is in, instead of the abbr itself.
	   this keeps the description central when different line-heights are used in p / code / h1 &c. */
	color: #235; line-height: inherit;
	/* why have borders for something that appears to have none? 1. you may wish to customise, and
	   2. without the border, the shadow appears detached from the edge! */
	border: 1px solid #93a3b9 !important;
	/* gecko supports % for border curves, allowing us to have a perfectly round caps regardless of size */
	-moz-border-radius: 0 50% 50% 0; -moz-box-shadow: -1px 2px 1px rgba(0,0,0,.5); 
	/* webkit does not support % for radius, for small fonts the curves disappear.
	   8px curves will support a minimum 11px font-size. a solution to this is to set line-height to 1.5em
	   (instead of inherit) above, and then use 0.75em instead of 8px below. this will work if you have a
	    strict line-height of 1.5em already through your document */
	-webkit-border-top-right-radius: 8px; -webkit-border-bottom-right-radius: 8px;
	-webkit-box-shadow: -1px 2px 1px rgba(0,0,0,.5); background-color: #93a3b9;
}

/* the abbreviation description */
abbr:nth-child(n)[title]:hover::before {
	/* write out the abbreviation description */
	content: attr(title);
	/* the controversial bit */
	position: absolute; display: block;
	/* top has to be nudged up because this is *inside* the abbr whose 1px border is offsetting it.
	   `right: 100%` positions the description hanging out to the left regardless of description width */
	height: 100%; top: -1px; right: 100%; padding: 0 5px 0 7px;
	/* because of `right` above, the width is technically 0px, therefore `nowrap` is required! */
	color: #456; white-space: nowrap; text-indent: 0; border: 1px solid #c3d3e7;
	-moz-border-radius: 50% 0 0 50%; -webkit-border-top-left-radius: 8px;
	-webkit-border-bottom-left-radius: 8px; -webkit-box-shadow: -1px 2px 1px rgba(0,0,0,.5);
	-moz-box-shadow: -1px 2px 1px rgba(0,0,0,.5); background-color: #c3d3e7;
}

I strongly suggest that before putting this code to use, you read up on the proper way to use the abbreviation element. In short, abbreviations are not for defining terms, but rather expanding on how a piece of text should be read according to the author’s original intentions.

This code is licenced under a Creative Commons Attribution 3.0 licence; please share and remix this, just include “Kroc Camen” and/or “camendesign.com” in your stylesheet comments, thanks.