Using combination of CSS transforms, transitions, gradients and :target it’s possible to create things that usually require JavaScript — such as accordion and reflections (in Firefox). Unfortunately, this only works 100% in Safari and Chrome (and I guess any other webkit using browser). In Firefox these elements behave properly, they just don’t have animations or gradients. 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 attention that Safari in fact supports actual css reflections in the form of –webkit-box-reflect. It’s very cool as it actually allows for dynamic reflections and you can even reflect video. It is however, much like gradients, a webkit only property and so doesn’t do anything for Firefox reflections.
Reflections
Simple reflections are very easy — make a copy of the content, flip it, shift it down and reduce opacity.
To make a copy of the content you have two options, in some cases you can use CSS, and in some you just have to create the content twice in HTML — this may seem “wasteful” but if you think about it, any reflections, JavaScript or otherwise (even native, when/if they come) involve duplicating the content — that’s just the nature of the beast.
To duplicate content in CSS we need to use :after and content (it is somewhat limited 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 current element, and content can take text in quotes or a number of elements (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 obvious limitation here is that content can’t contain tags, but it can contain URLs to other content (such as image). I also couldn’t see any way to use the text inside the tag (i.e. innerHTML), which I think is an unfortunate omission since that could’ve been quite useful.
The :after element can be styled just like anything else. So, here is the complete 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 element, modified its opacity and ‘flipped’ it using scaleY. Here is the spec for –moz-transform, it seems to be identical to Safari/Chrome implementation, they just use the –webkit– prefix instead.
Apparently IE has transforms, using filter property. So if you want to use this and need to make this IE compatible, you should be able to achieve at least some of these effects.
The last touch to make reflections perfect is the fade-out, but it’s only possible in Safari and only on solid background. To do this we need to use gradients. Basically you just put a short div alongside the bottom of the container (with the reflected objects) and make it fade from transparent to the background colour (from top to bottom). This will cover part of the reflection and make it look like it fades out. Here is the complete 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 breakdown of –webkit-gradient. The short of it is — the first attribute can be “linear” or “radial”; the next two pairs tell the gradient where it goes from and to (if I were to use ‘left bottom, right top’ it would go in a diagonal), next is any number 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 shortcuts for “color-stop(0,#xxx)” and “color-stop(1,#xxx)” respectively). The thing to note here is that I use rgba colour format, instead of hex, because “transparent” (for the second colour) doesn’t seem to work quite as well — the gradient doesn’t transition properly from transparent to opaque for some reason. Although I think I probably 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 animation — as mouse hovers over a menu item it grows a bit. This works in all browsers (since it’s just a font increase) but it’s only animated in Safari/Chrome. The code for that is simple — just add –webkit-transition to the initial style, and in the :hover style make the changes you want, and they’ll be animated. 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 property of transition specifies what property triggers the animation. So in this case all the attributes, that were changed (on :hover), will animate but only if font-size is among them. You can use “all” to specify that any attribute can trigger the animation. The next property is the time of the animation, and the last one is the timing function. Here is a nice break down of that. I could’ve used scale in this case (instead of font-size), but it didn’t maintain the correct vertical alignment, so I kept it as is. (I change the padding as well to prevent too much shifting around of the other menu items.)
If you check out the example page, you’ll see that there are two menus. The reason for it is that the first one seemed better in my head, but in practise turned out to be more annoying. So I created a variation of it that I think works better (it uses fixed item width), but I decided to leave the first version anyway.
Accordion
Accordion uses the :target trick. You know those in-page links like <a href=‘currentpage.html#top’> — apparently, when an item is activated using a link like that it gets the :target property. In other words, let’s say you have a header like this
<h2 id='header'>Some title here</h2>
and at the bottom 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, whatever styles are defined in h2:target will be applied to the header.
So then if instead of a header you have a content block with default height/width of 0px, and that block changes its width/height using :target style, then you’re essentially ‘showing’ the content ‘on click’ — pretty nifty ain’t it. Taking it a bit further, stack up (vertically or horizontally) a bunch of links and divs, and have the links target the divs — and voila, you’ve got yourself an accordion.
So that’s how I did the accordion on the example page. The extra “flare” that I added to it is vertical text (using transform, rotate(-90deg)), and for Safari, I also used –webkit-gradient to make the accordion tabs a bit nicer, and –webkit-transition to animate the “opening” and “closing” of the content divs.
Finally, that “gallery” at the bottom of the page uses more or less the same tricks as the menus. I did however run into a problem with it — I couldn’t centre (horizontally) the images, while keeping them “on the floor”. To put them there I had to use absolute positioning (vertical-align:bottom didn’t do the trick for some reason), which doesn’t work with auto-centring (image widths are not consistent) using margin:auto or display:inline-block. If somebody figures it out please let me know.
I find it very cool that all that with new CSS techniques it’s possible to do many of these things without any JavaScript.
I only hope that Mozilla adds transitions and gradients soon to their CSS engine.
