The other day I encountered a small but very interesting effect, visible in Bitbucket issues’ table. Some of the cells were slightly too narrow for the text they contained, and it had to be ellipsized. Usually this is done by cropping some of the text’s trailing chars and replacing them with dots – mostly because that’s what the
text-ellipsis style is doing. Here, however, I saw something much more original: the text was fading out in gradient-like style, going from full black to full transparent/white over a distance of about 30 pixels. It made quite of an eye-catching effect.
So, I decided to bring up Firebug and find out how this nifty trick actually works. Taught by past experiences, I expected a tightly coupled mis-mash of DOM and CSS hacks, with lots of moving parts that need to be carefully adjusted in face of any changes. Alas, I was wrong: it turned out to only use CSS, in succinct and elegant manner. After simple reverse-engineering, I uncovered a clever solution involving gradients, opacity and
:after pseudo-elements. It definitely deserves some press, so let’s look into it.
As far as I am aware, there is no portable way to do graphical post-processing of element’s content using just CSS attributes (IE-specific
filter styles and early CSS4 drafts notwithstanding). In other words, CSS is far below in sophistication and power when compared even to first generation of pixel shaders – an ancient technology from two decades ago. Any techniques that appear to use such magic anyway – like multiplicative blending of text with gradient texture – are therefore just faking it, most likely with smart tricks involving overlapping layers, images and backgrounds, carefully positioned one over another.
The gradient-fade text ellipsis, which I’m describing here, is no different. It uses the sole type of per-pixel transformation that is easily accessible in present-day CSS: alpha blending. Thanks to years of standarization and growing pressure on browser vendors, the power of opacity control is now at our disposal, ready to take us into the semi-transparent future…
But while the simple
opacity property is often the exact thing we need, it’s not the case if we want to apply a gradient over text. Mostly because it wouldn’t be really smooth (especially for larger font), and partially because of the ugly markup it would result in, with every character wrapped up in its own
<span>… Eww! Surely there must be a better way.
And indeed, there exists a much cleaner solution. It involves placing an additional layer over the text – one with an actual gradient background. In CSS3, it is possible to generate such background on the fly by using the
This will create a pattern going (left to right) from transparent to opaque white. When applied to a block element, this style turns it into a “dimmer” which can be placed over content that we want to be fading out. Provided we do this correctly, the browser’s alpha blending mechanism will take care of rest.
linear-gradient is a CSS3 feature. For maximum compatibility, you should include the vendor-specific versions of it, prefixed by
-ms-. This is yet another reason to use a CSS preprocessor such as LESS.
CSS veterans probably know all crooks and crannies of the infamous
position property and the exact rationale behind choosing specific value for it. Mere mortals (like us) just vaguely remember some tidbits, such as the need of using
position:absolute if you want to organize your content into layers. That’s just part of the puzzle, though – you also need
position:relative on the parent element:
Specifying 0 for both
bottom will automatically set the correct height of our
.dimmer element, while
right:0 will align it properly. Its width can be specified as either percentage or
px; this adds some flexibility should we want to apply this effect to elements of varying sizes.
Our solution is not yet complete, as one inconvenience still remains. We require the
.dimmer element to be included in HTML, nullifying our effort to only use CSS for the effect. Thankfully, it isn’t quite a big of an obstacle as it may look at first glance.
Yet another CSS feature comes handy here: the
:after pseudo-elements. Essentially, they give us two “free”, virtual children for any DOM element – a first and last one, respectively. Coupled with
counter-* properties, they allow for some really clean and powerful constructs, such as automatic insertion of quote chars around given text or out-of-the-box numbering (without
But for our gradient trick, we don’t need these features. We just want to turn one of those pseudo-elements into functional equivalent of the
<div>, so we don’t have to insert it into DOM anymore. As it turns out, this isn’t at all difficult:
Here we used the
:after element as it seemed more logical, but it is perfectly valid to style the
:before one instead. The important bit is getting rid of the inner
<div>: since we use a pseudo-element, it is not needed anymore. Our implementation is now purely CSS-based.
In its current incarnation, CSS is not nearly as powerful and convenient as we (especially programmers) would like it to be. Still, it might allow for neat & tidy solutions – if we know where to look for relevant features and have some creativity to assemble them together.