HTML5 has a new attribute, contenteditable, which can be applied to any element which allows it to be edited directly in the browser window. Think of text input with a predefined value, but it can literally be any element. Form elements like text inputs support the :focus pseudo class, which allow us to style those elements when they are clicked upon or otherwise navigated to. Giving an element the contenteditable attribute means it also now supports the :focus pseudo class, which opens up some interesting possibilities!
We’ll exploit this little trick to make an expanding image (like a lightbox without the overlay) right within some content.
UPDATE: Even better, you can give attributes a tabindex attribute, like you would a form element, which allow allows :focus without the editability. This article has been updated to go that route instead.

HTML5 Markup
HTML5 has nice elements for including captioned images.
<section class="image-gallery">
<figure>
<img src="images/img-1.jpg" alt="jump, matey" />
<figcaption">Jump!</figcaption>
</figure>
</section>
We just give the figure element tabindex, so that it can be in focus.
<figure tabindex=1>
Give each subsequent figure a tabindex value one higher and the images will be able to be tabbed through nicely from the keyboard!
The Images
The images will be “full size”.

This means they will be scaled down for regular display on the page, and scaled up when we do our magical CSS expanding. Potentially a waste of bandwidth for the browsers that don’t support this. The tradeoff is your call.
The CSS
Normal display:
figure {
width: 120px;
float: left;
margin: 0 20px 0 0;
background: white;
border: 10px solid white;
-webkit-box-shadow: 0 3px 10px #ccc;
-moz-box-shadow: 0 3px 10px #ccc;
-webkit-transform: rotate(5deg);
-moz-transform: rotate(5deg);
-webkit-transition: all 0.7s ease;
-moz-transition: all 1s ease;
position: relative;
}
figcaption {
text-align: center;
display: block;
font-size: 12px;
font-style: italic;
}
figure img {
width: 100%; /* Scale down */
}
The :focus part of this isn’t CSS3, the but shadows, transforms, and transitions are. The hover state will rotate the image a bit, and the :focus style (when the image is clicked on), will expand it, rotate it again, and make sure it’s on top with z-index.
figure:hover {
-webkit-transform: rotate(-1deg); -moz-transform: rotate(1deg);
-webkit-box-shadow: 0 3px 10px #666; -moz-box-shadow: 0 3px 10px #666;
}
figure:focus {
outline: none;
-webkit-transform: rotate(-3deg) scale(2.5); -moz-transform: rotate(-3deg) scale(2.5);
-webkit-box-shadow: 0 3px 10px #666; -moz-box-shadow: 0 3px 10px #666;
z-index: 9999;
}
Browser Compatibility
The HTML5 contenteditable attribute is supported in Firefox 3.6+, Safari 4+, Chrome, and Opera (10.6 only tested). Note that the attribute will cascade down to all child elements. In our demo, having the image and figure caption be editable doesn’t make much sense, so we can turn it off individually on them.
<figure contenteditable="true">
<img src="images/img-1.jpg" alt="jump, matey" contenteditable="false" />
<figcaption contenteditable="false">Jump!</figcaption>
</figure>
If you forget to turn off the editablity of the images, Firefox can look a bit funky giving you resize handles for images. Update: another reason to use tabindex instead.

Opera respects the child elements not being editable, but still runs the spellchecker on them and will red-underline words it finds misspelled which can be a bit weird.
Our demo also relies upon transforms to “work” though, which are only supported in current WebKit browsers and Firefox 4+. Firefox 3.6 supports the transform but not the transition.
The fallback is that the images just don’t expand though, which is of course no big deal.
Demo & Download
Related
- If you are looking for a pure CSS lightbox, including the overlay, check out Stu Nicholls version.
- If you need your lightbox to be fully cross browser compatible, use JavaScript like the Colorbox plugin for jQuery.
Credits
Big thanks to Adrian Sinclair who contacted me about this idea and helped with everything.
cool
can we rotate the image in z-coordinate (3D) ?
if i ain’t mistake, using
-moz-transform: matrix(a, c, b, d, tx, ty)
as far as i know, 3d transformations are in the spec but not available in any stable browser so far
Not so downloadable for now… ;-)
Thanks anyway.
I personally do not like this technique because you can remove the images from the light box (because of the content editable attribute).
But is still interesting, yet another thing you can do without Javascript.
Gotta love it…and no JS!
the contenteditable has some unwanted repercussions, like when u click the image and hit a key, like “a”, the image will be replaced by a character “a”
instead of using contenteditable, consider using tabindex=”-1″, which should also make it focussable (seems to work on a Chrome 6)
Great idea! Browser support seems to be the same. I updated the article and download to use this instead, as it’s much cleaner and adds keyboard navigation quite cleanly. I figured I’d just use tabindex values of 1-4 instead of -1, since why not have them be able to be tabbed to…
Good idea to use :focus for giving specific styles, and nice demo.
But contenteditable is the WRONG attribute to use if you want to make a random element focusable. You should be using tabindex=”0″ instead. Look it up. :)
Oh, I see that someone mentioned tabindex before. Using tabindex=”-1″ (or any negative value, says HTML5) will work but the element won’t be reachable through sequential focus navigation, while with tabindex=”0″ it will. So use tabindex=”0″ if you want it to work with focus navigation (rather than clicks).
Great article. I didn’t know about either contenteditable or tableindex. You’re demo isn’t quite as exciting as it is in IE. Poor poor IE. When will it catch up?
This article makes great use of CSS3 features, that’s awesome. So when do you think we should make the final move onto CSS3 given today’s significant statistics of IE users?
I’m not sure if this is relevant, but there’s something called CSS3PIE which contends to use javascript in order to make CSS3 features IE-capable. However, my experience with it was rather terrible (aka, I couldn’t get it to work).
PIE only handles 3 CSS3 properties I believe…border-radius, linear gradient, and box shadow. And I got it to work just fine. It has issues though with z-index, and I am pretty sure it doesn’t work at all on form elements.
The main thing to remember with PIE though, is that even though you are declaring it in the CSS file, it HAS to be relative to the html file. So instead of (../images/PIE.htc), it would have to be(images/PIE.htc). I’m guessing that is the problem you are having
It’s possible that this is where my flaw was. However I did read those instructions in the PIE manual and did make the path relative to the HTML document.
I’ll give it another shot though. Thanks for the advice
Ahh I found the reason why! It’s because it doesn’t work locally (for some reason, even though it should given it’s only JS?) But when I put it on my server, it works just fine.
Cool stuff :)
PIE is *the* solution for IE css3. Highly recommended.
So I’m assuming the anti-aliasing issues largely stem from subpixel rendering technologies not being as bleeding edge as HTML5, right? Because even with ClearType the fracturing looks awfully unprofessional. Now don’t me wrong, I like the concept, but like most things HTML5 it’s just not ready for mainstream usage.
The aliasing is an issue with Chrome (and Webkit presumably) on the images and with Firefox on the text (but interestingly, Chrome shows the text fine and Firefox shows the images nicely).
It’s not really anything to do with subpixel rendering – they simply aren’t anti-aliasing rotated images (Chrome) or text (FF). The jagged edges are aliasing artefacts.
Amazing example, with no JS, only pure html5 and css3 – i love that. Thank you.
Do most CMS editors have the ability to resize an image one a user places it? If not, then that could be a use for the contentedible on an img. Let’s say the client uploads a freakin huge image, then places it in the content, and you have it so that all img tags get contenteditable set to true, you could use javascript to grab the height and width after they resize it with the bounding box, and use those numbers for what size the img displays at. Make sense?
And does anyone know if the HTML5 IE shiv handles these new attributes? Or is it just elements? I haven’t used it, and only heard of it. If it does, you could use CSS PIE, and the shiv, to get everything but the CSS3 transform to work in IE then. At least that is what I am thinking.
Of course that is a lot of crap to go through just to get IE to work somewhat like the other browsers.
Ok, in reply to myself, and anyone else that cares. The shiv does indeed get the attributes to work. For IE7, if you have contenteditable set to true on an image, you need a min-height on it or you won’t be able to adjust the img height wise. Didn’t try it in 6. IE8 works fine without the min-height.
And ignore the “could use PIE and…” in my comment above. Mind blanked there. Without transform, having the focus available doesn’t mean crap.
Thanks for looking up whether shiv handles the new attributes, was wondering about that one.
Great article btw Chris!
Sorry, but why aren’t you adding -o- and unprefixed rules?
This way when browsers support the properties without prefixes you won’t see the effects!
I came here to ask this. Is ‘transform’ instead of just ‘-moz/-webkit-transform’ a good way to future-proof?
Couple of points to note from the HTML5 spec:
tabindex="-1" is to allow focus on elements using JavaScript and mouse clicks but not by hitting tab.
Positive integers are fine, as are all zeros if the elements are in order in the HTML.
The second point I would like to make is that the use of the figure/figcaption combo is debatable.
Here’s what the spec says about figure:
Figure can contain images, videos, tables, code snippets and more. Also, it is sectioning root which means it can have its own outline i.e. headings and sub-headings, sections and articles if need be.
Apart from section being used where div should be, wrapping all images in figure could possibly be the most common mistake in HTML5 code.
I guess I’m not really feeling any particular element is being abused here… but thanks for the research on the spec, it’s definitely worth considering.
It’s also interesting that the HTML5 spec has been adapting itself to how people are using it still. The aside element was for a while not cool to use for the sidebar, but is now allowed.
Absolutely. The great thing about HTML specs is that implementation by and large comes first, and so it should.
I put down a few thoughts on figure based on my understanding of the spec at http://derekjohnson.posterous.com/, but if captioned images are generally marked up with figure/figcaption I would support the spec changing to reflect that.
The last thing we want is HTML5 to end up in the same place as XHTML2.
P.S. Thanks for sorting out my over-enthusiastic use of code tags.
I’m with Chris, what could possibly be more semantic for an image with a caption or label than <figure> and <figcaption>. I definitely don’t think every image should be wrapped in <figure>, but if it is paired with a caption then it makes more sense to have
Your point about using all zeros for tabindex is a good one, since i found that tabindex could be used instead of contenteditable (what I originally used) I thought it would be good for usability to be able to tab through images since people using screen reader would probably make the most use of tabbing through things and if they have poor vision (likely if they are using a screen reader) they need image zooming the most.
To echo the comments on tabindex of zero, using zero for standard tab order is much easier to maintain. Attempting to track the current tab index value relative to all focusable elements is a painful exercise particularly when mixing both ARIA and standard widgets.
I found a way to make this work in ie if javascript is enabled, cssSandPaper is a css “plugin” in javascript that allows transforms in ie, using filters (those thing used to make ie6 load png24 and for opacity in ie), I’m not quite sure how to use it, but it’s worth a shot!
Liking this, especially with the tabindex improvement.
A timely reminder too that somehow I need to override WordPress gallery output to use this structure (I’m not a programmer, PHP or otherwise).
Thanks, Karl
Very good idea.
Thank you for this article.
I try to improve it giving an overlay effect in background :
http://www.creativejuiz.fr/trytotry/lightbox-with-tabindex-and-css3/
Hope you’ll visit it.
Thanks, Geoffrey.
Hey Chris! Great article! But a little grammar mistake :)
“The :focus part of this isn’t CSS3, the but shadows, transforms, and transitions are”
Great article though! Thanks!
Why you’re not using -o-transform, -o-transition and box-shadow properties to make this work on Opera too?
I have all those prefixes in the demo now, and literally none of them work in Opera 10.6
They work well for me (Opera 10.60 on Debian Linux) Have you tried flushing cache or reloading the CSS file? Go to https://css-tricks.com/examples/ExpandingCaptionedImages/css/style.css and press F5 (or what the command is on Mac, maybe Cmd+R) once or twice, that worked for me.
And… CSS property
-o-box-shadow
doesn’t exist, Opera’s using regularbox-shadow
(that also applies toborder-radius
, but not totransform
andtransition
– they’re using-o-
vendor prefix.).Hey Chris,
Nice tutorial :)
Great to see this being built without the use of javascript, don’t get me wrong, I really like javascript but reducing download time is starting to become really beneficial for SEO.
Such a shame about IE and html5/CSS3 :(
I would love to build sites using all of these new features but until IE catches up It’s a bit of a no go as a good percentage of web users still use IE and sites just would not look as good as I want them to. +most of the people that I design the sites for use IE so couldn’t really sell them the amazing features their site packs!
Looks like full on Wuffoo Battlefield!!! LOL
Nice article as always!!
Totally Awesome! Thank you :)
Maybe it was named before but I could not find it…
Safari 5 does anything wired with the shadow and the font… Let me call it “baa” (bad anti-aliasing).
The baa isn’t a Webkit-problem I think because it does not appear in Chome 5. It comes up when the transition-methode is called. After the animation everything is great again.
Anyway. It’s great. Thanks, Chris.
I think this has the potential to actually have some basic Internet Explorer functionality. Internet Explorer 8 will apply :focus to any element (not just inputs) so it will work without the tab index.
It would take some doing (lots of IE specific margins and positions), but I threw this together real quick and it works just fine in IE8. With some work, you could probably achieve the same level of support you’re getting in Firefox 3.5.
http://jsfiddle.net/kPpq8/1/
Fantastic, as always, Chris. I tried it out on a page from data acquired in Northern Minnesota this July. (I now live in NYC and the heat was killing me.)
Funny thing, when I added a Vimeo video link, the Zoomed type does not Focus in Safari. But it does in Firefox.
See here: http://kennethbsmith.com/bonus/holidays/mn0710/ (without video)
and with video: http://kennethbsmith.com/bonus/holidays/mn0710/index02.html (caption type on Zoomed photo is Wack)
What could cause that? Why would a plug-in to that?
Hope you are having a great summer cause you deserve it. Thanks for the tutorial.
PS: I still haven’t heard if Brett is playing this year, have you?
Firefox 3.6 have no animation. Chrome 6 has no antiantlias. Half of visitors (IE) can’t see at all.
How about using Flash that has everything and work in 98% of browsers? And do a simple fallback instead of mess up everything?
In Safari for IOS works nice but is not possible to return to normal scale!
Help!
Love this way of having expanding images however what if I want a hyperlink in the figcaption? Upon clicking on an anchor tag focus is lost and I have been unable to find a way to get it to work.