Creating Dynamic Transparent Border Overlays

One element of the new Opus design that — IMHO, of course — adds some really cool-yet-subtle eye candy are the semi-transparent borders that appear on many of the images (an example would be the thumbnails in the “Recent Music Reviews” and “Recent Movie Reviews” widgets over there the right column).

I quickly came up with the idea in my Photoshop mockup without giving much thought as to how I might actually make it work when I got to the HTML/CSS stage. But once I got there, I found myself in a bit of a quandary. I had done something similar in an earlier iteration of Opus, using a transparent GIF image to add rounded corners to some of the images (this is similar to the approach Facebook uses to add rounded corners to the avatars on your “Wall”).

However, that approach assumes that you always know the size of your images, that your images are always the same size. Which is the case with 80% of the images on Opus, but there are plenty of images whose sizes are all over the place. I’d either have to create overlay graphics for every size, or make sure that I always used the same sizes of images in my entries time and again. Which aren’t bad ideas in and of themselves, but I was looking for a little more flexibility.

Ideally, I wanted something that could be applied to any image of any size, be it a photo, some artwork from a CD, a movie poster, etc. After about 30 minutes or so, I came up with the following method. It’s by no means original, and there are some drawbacks, but it works for my purposes — maybe it’ll work for yours.

Now, if you’re like me, and you learn better by seeing something in action and breaking it down, I’ve created a very basic demo that shows the technique in action. For the rest of you, though, read on.

The HTML and CSS

First, I wanted to get my HTML and CSS in place. Only then could I start looking at ways to make things dynamic. Here’s the basic HTML that I came up with:

<span class="border" style="width: 500px;">
	<img src="foo.jpg" alt="" width="500" height="300" />
	<span style="width: 490px; height: 290px;"></span>
</span>

And here’s the sample CSS:

span.border {
	display: block;
	position: relative;
	margin: 0 0 1em;
}

span.border img {
	display: block;
	margin: 0;
}

span.border span {
	display: block;
	position: absolute;
	z-index: 100;
	top: 0;
	left: 0;
	border: 5px solid #fff;
	opacity: .5;
}

Let me explain what’s going on. First, I’m wrapping the image within a span tag that has the class of “border” and I’m setting the width of the span equal to the width of the image using an inline style. Then I’m putting another span tag directly after the image; this span’s width and height are both set to be 10 pixels less than the image’s width and height, respectively.

In the CSS, I’m specifying the “border” span to be a block element with relative positioning and a margin. The image has been set to be a block element as well with no margin. And finally, the second span is also made a block element and is positioned absolutely above the image.

The second span is also given a 5 pixel white border, which is why its width and height were set to be 10 pixels less than the image’s width and height — the border accounts for 10 pixels of width and height, thus taking up the extra space. And finally, its opacity is set to “.5” to create the semi-transparency.

My HTML and CSS were in place but I wasn’t done yet. After all, I didn’t want to have apply this extra HTML to every image on the site, either by hand or in my templates. I didn’t want it bloating my code when I could automate the process with a little JavaScript.

The JavaScript

I wanted to create a JavaScript that would take the HTML output by ExpressionEngine and automatically format it so that it looked like the snippet above. Fortunately, jQuery — my JavaScript framework of choice — has a handy set of DOM manipulation tools that can do the extra heavy lifting for me.

Here’s a sample of the JavaScript that I came up with:

$(document).ready(function() {
	$("img.border").each(
		function(i) {
			$(this).wrap('<span class="border" style="width: '+$(this).width()+'px;"></span>');
			var w = $(this).width()-10;
			var h = $(this).height()-10;
			$(this).after('<span style="width: '+w+'px; height: '+h+'px;"></span>');
		}
	);
});

The JavaScript starts by looking for all images that have the class of “border” applied to them using jQuery’s “each” function. This function creates an array of elements and then cycles through them. You can perform operations on each element of the array using the “this” identifier.

I use the “wrap” function to put each image inside the aforementioned “border” span and I create the inline style containing the image’s width using the “width” function.

Then I create two JavaScript variables — “w” and “h” — that contain the image’s width and height, respectively. I subtract “10” from each.

And finally, I use jQuery’s “after” function to inject the second span right after the image but before the “border” span’s closing tag. I create the second span’s inline style — which sets its width and height to be 10 pixels less than the image’s — using the “w” and “h” variables that I had created previously.

Cleaning Up

Now that my JavaScript is in place, I can clean up my HTML considerably.

<img src="foo.jpg" alt="" width="500" height="300" class="border" />

All I have to do is add a class of “border” to my images to achieve the desired effect, either by doing it manually or setting my EE templates to do so. My JavaScript and CSS will take care of the rest.

Also, if you want this technique to work in Internet Explorer, you’ll need to add some “special” CSS:

span.border span {
	-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
	filter: alpha(opacity=50);
}

IE doesn’t support the “opacity” CSS attribute. Instead, you have to use IE’s proprietary filters to control an element’s opacity. You can find more information here. (I recommend putting the above CSS in a second, IE-specific stylsheet and including it via conditional comments in order to keep things nice and tidy.)

Caveats

I’d be remiss if I didn’t mention a few caveats with regards to this technique:

  • If you compare the above code snippets to the code I’m actually using on Opus, you’ll see some differences. This is because I have several different image classes to control image alignment. Nevertheless, what I use on Opus and what I’ve outlined above are essentially the same technique.
  • As mentioned before, this technique requires jQuery. However, it should be relatively easy to port it to your JavaScript framework of choice.
  • Because this technique uses JavaScript to perform DOM manipulations, there can be some performance hits — especially if it’s applied on a page that contains many images.
  • I am not a JavaScript master. As such, I’m sure there are ways to optimize things further. What’s more, if you know of any, I hope you’ll share them in the comments below.
  • Some people might take issue with the markup I’m using. For example, it requires setting inline elements to be block elements. It also requires inline styles. Normally I try to avoid these things, but in this case, I took a more pragmatic approach. However, if you’re an absolute stickler when it comes to this sort of thing, then this technique may not be for you.
  • This technique has been tested in Safari 3 (Mac), Safari 4 (Mac), Firefox 2 (Mac/PC), Firefox 3 (Mac), IE6, and IE7. I’d love to hear what users of other browsers see.
  • Finally, I’m not the first person to do this. Do a search for “image overlays CSS” or something similar and you’ll find plenty of other ways for accomplishing this look. But this is the technique that worked for me.

Fin.

If you use this technique, let me know. I’d love to see it in action elsewhere on the Web. And of course, if you have any ideas for improving this technique, I’m all ears.

Enjoy reading Opus? Want to support my writing? Become a subscriber for just $5/month or $50/year.
Subscribe Today
Return to the Opus homepage