Reflections and Accordion using CSS only, in Safari and Firefox

Using com­bi­na­tion of CSS trans­forms, tran­si­tions, gra­di­ents and :tar­get it’s pos­si­ble to cre­ate things that usu­ally require JavaScript — such as accor­dion and reflec­tions (in Firefox). Unfortunately, this only works 100% in Safari and Chrome (and I guess any other webkit using browser). In Firefox these ele­ments behave prop­erly, they just don’t have ani­ma­tions or gra­di­ents. As for IE, I didn’t bother with it at all.

Here is the page I’m going to go over. Feel free to dig into the code and if you’ve got any ideas on how to make it even sleeker let me know.


Update: It’s been brought to my atten­tion that Safari in fact sup­ports actual css reflec­tions in the form of –webkit-box-reflect. It’s very cool as it actu­ally allows for dynamic reflec­tions and you can even reflect video. It is how­ever, much like gra­di­ents, a webkit only prop­erty and so doesn’t do any­thing for Firefox reflections.


Reflections

Simple reflec­tions are very easy — make a copy of the con­tent, flip it, shift it down and reduce opac­ity.
To make a copy of the con­tent you have two options, in some cases you can use CSS, and in some you just have to cre­ate the con­tent twice in HTML — this may seem “waste­ful” but if you think about it, any reflec­tions, JavaScript or oth­er­wise (even native, when/if they come) involve dupli­cat­ing the con­tent — that’s just the nature of the beast.

To dupli­cate con­tent in CSS we need to use :after and con­tent (it is some­what lim­ited though). Here is how I did it for menu links

<a href='#' title='Home'>Home</a></li>

.menu a:after { content:attr(title); }

:after tells CSS what to add after the cur­rent ele­ment, and con­tent can take text in quotes or a num­ber of ele­ments (W3C spec). In this case I use the title attribute of the link, which is exactly the same as the text of the link. The obvi­ous lim­i­ta­tion here is that con­tent can’t con­tain tags, but it can con­tain URLs to other con­tent (such as image). I also couldn’t see any way to use the text inside the tag (i.e. inner­HTML), which I think is an unfor­tu­nate omis­sion since that could’ve been quite useful.

The :after ele­ment can be styled just like any­thing else. So, here is the com­plete style for the menu item reflection

.menu a:after {
	content:attr(title);
	display:block;
	width:100%;
	position:absolute;
	bottom:-.88em; left:0;
	opacity:.2;
	text-align:center;
	-moz-transform:scaleY(-1);
	-webkit-transform:scaleY(-1);
}

As you can see, I shifted the ele­ment, mod­i­fied its opac­ity and ‘flipped’ it using sca­leY. Here is the spec for –moz-transform, it seems to be iden­ti­cal to Safari/Chrome imple­men­ta­tion, they just use the –webkit– pre­fix instead.

Apparently IE has trans­forms, using fil­ter prop­erty. So if you want to use this and need to make this IE com­pat­i­ble, you should be able to achieve at least some of these effects.

The last touch to make reflec­tions per­fect is the fade-out, but it’s only pos­si­ble in Safari and only on solid back­ground. To do this we need to use gra­di­ents. Basically you just put a short div along­side the bot­tom of the con­tainer (with the reflected objects) and make it fade from trans­par­ent to the back­ground colour (from top to bot­tom). This will cover part of the reflec­tion and make it look like it fades out. Here is the com­plete style for this div

.reflection-gradient {
	display:block;
	width:100%; height:1.6em;
	position:absolute;
	bottom:0; left:0;
	background:-webkit-gradient(linear, left bottom, left top, color-stop(.2, rgba(35,35,41,1)), to(rgba(35,35,41,0)));
}

Here is the break­down of –webkit-gradient. The short of it is — the first attribute can be “lin­ear” or “radial”; the next two pairs tell the gra­di­ent where it goes from and to (if I were to use ‘left bot­tom, right top’ it would go in a diag­o­nal), next is any num­ber of points where colour changes (i.e. it can be 3, 5, 20 points, etc.). In this case there are only two and they’re telling the browser that, from 0% to 20% of height use colour A, and at 100% height use colour B (“from” and “to” are short­cuts for “color-stop(0,#xxx)” and “color-stop(1,#xxx)” respec­tively). The thing to note here is that I use rgba colour for­mat, instead of hex, because “trans­par­ent” (for the sec­ond colour) doesn’t seem to work quite as well — the gra­di­ent doesn’t tran­si­tion prop­erly from trans­par­ent to opaque for some rea­son. Although I think I prob­a­bly could’ve used hex colour for the first value, but I decided not to mix them up.

One more thing I decided to add to the menu is a bit of ani­ma­tion — as mouse hov­ers over a menu item it grows a bit. This works in all browsers (since it’s just a font increase) but it’s only ani­mated in Safari/Chrome. The code for that is sim­ple — just add –webkit-transition to the ini­tial style, and in the :hover style make the changes you want, and they’ll be ani­mated. Here are the styles I used for the menu items

.menu a {
	display:block;
	position:relative;
	font-size:1.6em;
	padding:0 .8em;
	...
	-webkit-transition: font-size .1s linear;
}

.menu a:hover {
	font-size:2em;
	padding:0 .4em;
	color:#c8c8c8;
}

The first prop­erty of tran­si­tion spec­i­fies what prop­erty trig­gers the ani­ma­tion. So in this case all the attrib­utes, that were changed (on :hover), will ani­mate but only if font-size is among them. You can use “all” to spec­ify that any attribute can trig­ger the ani­ma­tion. The next prop­erty is the time of the ani­ma­tion, and the last one is the tim­ing func­tion. Here is a nice break down of that. I could’ve used scale in this case (instead of font-size), but it didn’t main­tain the cor­rect ver­ti­cal align­ment, so I kept it as is. (I change the padding as well to pre­vent too much shift­ing around of the other menu items.)

If you check out the exam­ple page, you’ll see that there are two menus. The rea­son for it is that the first one seemed bet­ter in my head, but in prac­tise turned out to be more annoy­ing. So I cre­ated a vari­a­tion of it that I think works bet­ter (it uses fixed item width), but I decided to leave the first ver­sion anyway.

Accordion

Accordion uses the :tar­get trick. You know those in-page links like <a href=‘currentpage.html#top’> — appar­ently, when an item is acti­vated using a link like that it gets the :tar­get prop­erty. In other words, let’s say you have a header like this

<h2 id='header'>Some title here</h2>

and at the bot­tom of the page you have a ‘top’ link like this

<a href='#header'>Back to top</a>

Then, when you click the “back to top” link, what­ever styles are defined in h2:target will be applied to the header.

So then if instead of a header you have a con­tent block with default height/width of 0px, and that block changes its width/height using :tar­get style, then you’re essen­tially ‘show­ing’ the con­tent ‘on click’ — pretty nifty ain’t it. Taking it a bit fur­ther, stack up (ver­ti­cally or hor­i­zon­tally) a bunch of links and divs, and have the links tar­get the divs — and voila, you’ve got your­self an accordion.

So that’s how I did the accor­dion on the exam­ple page. The extra “flare” that I added to it is ver­ti­cal text (using trans­form, rotate(-90deg)), and for Safari, I also used –webkit-gradient to make the accor­dion tabs a bit nicer, and –webkit-transition to ani­mate the “open­ing” and “clos­ing” of the con­tent divs.

Finally, that “gallery” at the bot­tom of the page uses more or less the same tricks as the menus. I did how­ever run into a prob­lem with it — I couldn’t cen­tre (hor­i­zon­tally) the images, while keep­ing them “on the floor”. To put them there I had to use absolute posi­tion­ing (vertical-align:bottom didn’t do the trick for some rea­son), which doesn’t work with auto-centring (image widths are not con­sis­tent) using margin:auto or display:inline-block. If some­body fig­ures it out please let me know.

I find it very cool that all that with new CSS tech­niques it’s pos­si­ble to do many of these things with­out any JavaScript. :) I only hope that Mozilla adds tran­si­tions and gra­di­ents soon to their CSS engine.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>