boogdesign posts

Longer posts on standards based web design, portable web development and Linux, intermingled with some stuff on my other nerd interests.

Rob Crowther, London, UK based Blogger, Web Developer, Web Designer and System Administrator - read my Curriculum Vitae

Buy my book!

Book CoverHello! HTML5 and CSS3 available now

Buy my other book!

Book CoverEarly access to HTML5 in Action available now

Category: Standards, HTML and CSS

19/01/10

11:56:15 pm Permalink London Web Standards: Web Fonts with Ben Weiner

Categories: Front End Web Development, Standards, HTML and CSS

Review: LWS January: Web Fonts with Ben Weiner at The Square Pig, 30 - 32 Procter Street, Holborn, London, WC1V 6NX 19:00 to 20:30

I've been experimenting a bit with @font-face recently so I was intrigued when I heard about this talk. It was booked out within hours of announcement but fortunately (for me) a few folk couldn't make it and the people on the waiting list got to attend.

Jeff has already done a 'live blog' of the talk, and there's a full transcript on Ben's site, so I'll just give a brief and slightly out of order outline and concentrate on the bits I found interesting.

Ben is a typography geek who got into web design so he had a good understanding of both the typographical world and the history of web fonts. This he discussed for the first third of the talk - explaining what a font is, why font design is difficult, why typography is important and why, more than twelve years after Microsoft first brought web fonts into the world with IE4 and EOT, we might finally have a chance at a solution that works for users, designers and the font foundries. One of the interesting aspects he discussed here was ligatures - where two letters, when placed consecutively, are melded into a single symbol. This happens in English fonts for things like 'ff' and 'fi', although the individual letter shapes remain, but the effect is even more marked in Arabic scripts where the combined character looks significantly different to the two (or more) which it represents. Having to deal with all this sort of stuff is why font design is so difficult, why even the 'open source' fonts have mostly been paid for (one way or another) and why font foundries have been generally paranoid about the possibility of all their hard work being stolen as soon as it's uploaded on to the internet.

Although most of the excited noises about web fonts are coming from designers, there are a few reasons why they're important. Firstly, while the font support for English and other languages based on the Roman alphabet is good, there are others where it's not so good and some (relatively popular) languages which have no font support at all in most operating systems (Telugu, a language of the Indian subcontinent, has 74 million speakers but no support in Windows). In this situation many sites have resorted to delivering their textual content as images. Delivering textual content as images is also an issue for accessibility as many designers, desperate to use their fonts of choice, resort to image replacement techniques which, if done badly, can result in poor accessibility.

This leads neatly in to Ben's discussion of the various hacks people have used because of the lack of working, cross browser web fonts. In order from the stupidest to the cleverist:

  • Image replacement - Basically, write your text in Photoshop and save it as an image, then put it in your page. An accessibility nightmare, particularly if the designer 'forgets' to provide the alt content.
  • Fahrner image replacement - In this technique the normal text is left in the HTML and the 'rendered' font is put in a background image. CSS is then used to move the normal text out of the viewport, leaving only the background image. Less stupid than straight image replacement, but still not perfect - no support for text resizing and you have to generate all your images in advance.
  • sIFR - Moving on to the clever hacks, sIFR works by embedding your font into a Flash movie then using Javascript to replace the headings dynamically. This was the first hack to be CMS-friendly - the text to be displayed is a run time parameter, but it introduced the additional requirements of JS and Flash.
  • Cufón - Getting to the first of the really clever hacks, Cufón uses a generator to convert your font into a set of JSON encoded outlines and then uses canvas to render it in the page.
  • Typeface.js - The cleverest of all, by making use of a server side component with access to the Freetype library, and then applying according to your existing CSS.

While these libraries get the job done, they do have a number of drawbacks - sIFR's use of Flash, Cufón's requirement to specify your fonts outside of CSS, the way it splits the words up into separate elements for each letter, and the general dependence on Javascript. They also have some drawbacks on the typography front, particularly to do with ligatures and letters that change shapes in different contexts - Ben showed us a number of example slides which you can check out on his site. Also, none of the use 'real fonts' - the same things that any other application would recognise as a font file.

We moved on to web fonts and the @font-face rule. Here is what it looks like:

@font-face {
	font-family: 'CantarellRegular';
	src: url('Cantarell-Regular.eot');
	src: local('Cantarell Regular'), 
		url('Cantarell-Regular.ttf') format('truetype');
}
You can then reference your downloaded font like this:
p { font-family: "CantarellRegular";}

All relatively straightforward, this will work in recent versions of all the major browsers - a standards compliant way to render actual fonts of your own choosing, with no need for any scripting or third party plugins, taking full advantage of typesetting capabilities already available within browsers and operating systems. Of course it's not quite that straightforward, there is some server side setup to consider, and then the ideological and technical hurdles that still need to be overcome before this all becomes practical for mainstream sites.

To deal with the idealogical problem first - font foundries are still not playing ball. This is a problem because, unlike music, a font is software rather than data and gets the same protections, also, as should be clear from some of the difficulties described above, good font design is difficult and so professional designers realise it is worth paying for a good font. So, even though the technology and standards are now getting in to place, font foundries are still unwilling to license 'desktop' fonts for distribution with websites.

And it's not like there are no practical problems. First you've got to consider what adding a few fonts to the mix is going to do to your bandwidth requirements. Regular desktop fonts, with just normal, bold, italic and bold/italic options, exceed 100k in size, especially if they include a full unicode character set. A font from a foundry, which is likely to have many more variations (extra heavy, expanded, smallcaps etc.) can be up to 1Gb in size. This gives you another thing to think about - what will the user see while waiting for the font to download? In Firefox they'll see the page rendered in whatever the default font is - this is likely to have a different geometry to the font you're delivering, so the page will have to be reflowed when the font arrives. This causes usability difficulties as elements users have started interacting with suddenly move around. In Safari the user will see nothing until the entire font is downloaded, making it impractical to use a large font for body text. This is the flash of unstyled text problem. You might be able to subset the font to reduce download times, but then you need a font with a permissive license, which leaves you back at the idealogical roadblock outlined above. You might have the idea of a single repository for web fonts to increase the chance users already have the font you want to use, similar to Google's Ajax library (and you wouldn't be the first), or even just to share a font across multiple domains in a portfolio. In this case you've got to set up Cross-Origin Resource Sharing for Firefox.

Although @font-face itself has wide support there are some differences in the details when it comes to defining families of fonts. Firefox seems to do pretty well and Safari/Chrome behave similarly, there are some issues that crop up in Opera and Konqueror, but most of the problems occur in IE. Here is how you declare a font family according to the spec:

@font-face {
    font-family: "DejaVu Serif";
    src: url("/fonts/DejaVuSerif.ttf") format("TrueType");
    font-weight: 400;
    font-style: normal;
}
@font-face {
    font-family: "DejaVu Serif";
    src: url("/fonts/DejaVuSerif-Italic.ttf") format("TrueType");
    font-weight: 400;
    font-style: italic;
}
@font-face {
    font-family: "DejaVu Serif";
    src: url("/fonts/DejaVuSerif-Bold.ttf") format("TrueType");
    font-weight: 700;
    font-style: normal;
}
@font-face {
    font-family: "DejaVu Serif";
    src: url("/fonts/DejaVuSerif-BoldItalic.ttf") format("TrueType");
    font-weight: 700;
    font-style: italic;
}
Each different weight and style has it's own definition and associated font file - multiple font files can be listed and the browser should pick the first format it can handle. However, of the above, this is all IE understands:
@font-face {
    font-family: "DejaVu Serif";
    src: url("/fonts/DejaVuSerif.ttf");
}
@font-face {
    font-family: "DejaVu Serif";
    src: url("/fonts/DejaVuSerif-Italic.ttf");
}
@font-face {
    font-family: "DejaVu Serif";
    src: url("/fonts/DejaVuSerif-Bold.ttf");
}
@font-face {
    font-family: "DejaVu Serif";
    src: url("/fonts/DejaVuSerif-BoldItalic.ttf");
}

Internet Explorer ignores all the font-weight and font-style rules so essentially all that happens (leaving aside the issue of file format) is that DejaVu Serif gets redefined repeatedly. A common work around is to define each font style explicitly in it's own font family, but this is hardly ideal.

In the final part of his talk Ben moved on to WOFF (Web Open Font Format) - the new standard which could finally get the font foundries behind web fonts. It was initially worked on by Erik van Blokland and Tel Leming, two guys well respected in the world of type, then worked on by Jonathan Kew and John Daggett of Mozilla, two guys with a lot of respect in the web browser world - so it was able to gain a lot of traction in both areas very quickly. So the future looks good, in the meantime there are a number of startups and other websites looking to exploit the demand for web fonts in TTF and EOT format:

This was a very useful talk on what is a hot topic in the web design world right now, I was particularly interested to learn about some of the history behind web fonts and some of the issues surrounding support of non latin languages, so 5 out of 5.
Technorati tags for this review:      

Tweet this!
1 feedback »PermalinkPermalink

25/12/09

12:45:02 am Permalink An Early Xmas Present - Opera 10.50 Pre-alpha

Categories: Front End Web Development, Standards, HTML and CSS

Opera gave web developers an early Xmas present this week when they released a 'pre-alpha' of Opera 10.5 for Windows and MacOS. The main new features are a new Javascript engine, Carakan, and a new vector graphics backend, Vega, for their Presto 2.5 rendering engine. With these in place Opera catches up with Gecko and WebKit in its support for, among other things, border-radius and CSS Transitions - which I covered in my first CSS3 post back in July. With that in mind I thought it would be worth updating my earlier examples with the Opera syntax to see how things looked.

Opera 10.50 uses the (soon to be) standard border-radius instead of a vendor specific property:

#mybox {
    border: 2px solid #666;
    overflow: hidden;
    -moz-border-radius: 20px;
    -webkit-border-radius: 20px;
    border-radius: 20px;
}

After changing all the examples, my first border test page looked like this:

Opera 10.5 showing borders test page

The bad news is Opera has the same problem as Firefox in that it doesn't clip the contents of an element you've rounded the corners on, so you can end up with square corners sticking out through the rounded ones. It also treats the corners where only one side is 'thick and rounded' badly, similar bot not quite the same as Safari:

Image of Opera's poor handling of rounded corners where one border is zero width

So our third implementation of border-radius manages to be subtly different from both existing implementations, though we can hope they all converge with time (this is pre-alpha, after all). I moved on to my borders and tables example page to have a look at how Opera handles box-shadow. Again, Opera implements the standard property, so it would have worked without any effort on my part if I'd just bothered to add the standard code in the first place &#59;) Here's a simple drop shadow on a table:

#table {
    margin-top: 1em;
    background-color: #ccc;
    border: 2px solid #666;
    -moz-border-radius: 20px;
    -webkit-border-radius: 20px;
    border-radius: 20px;
    -moz-box-shadow: 3px 3px 5px black, 3px 3px 5px black;
    -webkit-box-shadow: 3px 3px 5px black, 3px 3px 5px black;
    box-shadow: 3px 3px 5px black, 3px 3px 5px black;
}

Opera looks much the same as Firefox and Safari/Chromium:

Simple box-shadow on a table in Opera

Putting the box-shadow on the table cells also worked well, even with border-radius applied:

More complex box shadow with rounded corners

It also respects the infix value, like Firefox. Where it didn't look so good was if box-shadow was applied on :hover, it seems like it won't draw the drop shadow in any place outside of the original bounds of the element when the effect is applied dynamically:

Rendering problem when box-shadow is applied dynamically through hover

Finally, CSS Transformations. Opera 10.50 has similar support to WebKit, though this time through vendor specific properties, allowing both for transformations and animations. This code, in Opera and Chromium/Safari, will spin the mybox div through ninety degrees over the course of a second when you mouse over it(in Firefox, it will flip straight away):

#mybox {
    border-top: 40px solid #666;
    border-bottom: 40px solid #666;
    -moz-border-radius: 20px;
    -webkit-border-radius: 20px;
    border-radius: 20px;
    -webkit-transition-duration: 1s;
    -o-transition-duration: 1s;
}
#mybox:hover {
    -moz-transform: rotate(-90deg);
    -webkit-transform: rotate(-90deg);
    -o-transform: rotate(-90deg);
    -webkit-transition-duration: 1s;
    -o-transition-duration: 1s;
}

Have a look at my transformation examples if you're interested.

Overall I think this is very promising for such an early release, although I didn't do much browsing on the internet I didn't see any crashes while testing my examples above. Hopefully the glitches will be worked on as 10.50 matures and we'll have yet another browser in which we can play fancy CSS3 tricks.


Tweet this!
3 feedbacks »PermalinkPermalink

22/12/09

07:08:57 pm Permalink FizzBuzz in CSS3

Categories: Standards, HTML and CSS

I was reading Rachel Andrew's 24 ways post on Cleaner Code with CSS3 Selectors today, in which she covered nth-child selectors, and I was reminded of the FizzBuzz phenomenon which swept through Programming Reddit a couple of years ago. I thought it would be relatively easy to implement a CSS solution to the problem based on the nth-child selectors and generated content.

First I tried a quick test based on an ordered list and Rachel's example code for the table:

ol:nth-child(3n) li:after {
    content: "Fizz";
}

Using the advanced technique of copy and paste I quickly produced an ol element with 100 li's in it and observed the results. It wasn't quite a spectacular failure, but no Fizz appeared. A little reflection and I think I've figured out what I was doing wrong - the li is already the nth-child of ol and there are no further li child elements, so the rule doesn't match anything. After a bit of fiddling around I came up with two alternatives, cut out the li altogether:

ol :nth-child(3n):after {
    content: "Fizz";
}

Or use nth-of-type directly on li instead of nth-child:

ol li:nth-of-type(3n):after {
    content: "Fizz";
}

Then I realised that the ordered list was probably a mistake - I ought to be printing the number when none of Fizz, Buzz or FizzBuzz applies. My initial thinking was that having the number built into the markup would make it easier, but now I realised I'd have to both remove the default numbers and then insert new numbers in. So I switched to a table with 100 rows in it and started reading up on counters and numbering. This seems to work almost like a programming language, first you declare your variable on the root element:

table {
    counter-reset: number;
}

Then you stick it in a loop:

tr td:before {
    counter-increment: number;
    content: counter(number);
}

So now I have a table which counts up to 100, I override the generated content for each third and fifth child:

tr:nth-child(3n) td:before {
    content: "Fizz";
}
tr:nth-child(5n) td:before {
    content: "Buzz";
}

Finally specify the content for elements which are both a third and a fifth child:

tr:nth-child(3n):nth-child(5n) td:before {
    content: "FizzBuzz";
}

And, ta-da, FizzBuzz implemented in CSS (with the slightly unusual input requirement of a 100 row HTML table).


Tweet this!
Send feedback »PermalinkPermalink

21/10/09

02:50:26 pm Permalink Adventures in Web 3.0: Part 4 - Yet More CSS 3

Categories: Front End Web Development, Standards, HTML and CSS

In my last post on CSS3 I looked at scaling background images, RGBA colours and CSS gradients. In the early hours of Saturday morning it became increasingly apparent to me that I'd bitten off a bit more than I could chew in terms of a single post, as there were a few other things I wanted to cover. These were some practical layouts you could achieve thanks to these new features and some more details of cross-browser and backwards compatibility, which I shall now attempt to cover in this post.

I decided to see what interesting effects could can be achieved in terms of navigation menus, assuming the fairly standard 'list of links' markup. I began with some fairly innocuous markup in the 'navigation as list of links' tradition:

<div id="nav">
    <ul>
        <li><a href="#">Link One</a></li>
        <li><a href="#">2</a></li>
        <li><a href="#">Link Number Three</a></li>
    </ul>
</div>

You can see that there are three links/buttons with varying lengths. Traditionally this causes problems with image backgrounds which lead to using sliding doors and related techniques. The task I set myself was to see what I could achieve visually with this using only CSS, no additional markup allowed. My first idea was simply to use a 'shiny button' background image and scale it using background-size:

div#nav ul li {
    padding: 1em;
    background-image: url('bg-image-2.png');   /* Image courtesy of http://blog.entropiads.com/2009/01/29/free-3-d-glossy-button-icons/ */
    background-size: 100% 100%;
    -moz-background-size: 100% 100%;           /* Gecko 1.9.2 (Firefox 3.6) */
    -o-background-size: 100% 100%;             /* Opera 9.5 */
    -webkit-background-size: 100% 100%;        /* Safari 3.0 */
    -khtml-background-size: 100% 100%;         /* Konqueror 3.5.4 */
}
div#nav ul li a {
    color: #fff;
    padding: 1em;
}

It seems to work quite well:

Links with scaled background image

Note that this is doing something that you can't achieve with sliding doors - a typical sliding door image is uniform most of the way across, with something different in the first and last few pixels (eg. a rounded corner). So with a sliding doors approach as the button gets progressively wider the actual rounding becomes relatively smaller, whereas with a scaled background the rounding is relative to the size. This is something background scaling has in common with CSS gradients (ultimately they're just a different way to specify a scaled background) as we'll see below. This may or may not be an effect you want, I think if it's a more traditional sliding doors style look you're after then border-image is probably the thing to use.

For my next trick I decided to use CSS gradients to make some shiny buttons:

div#nav ul li {
    -moz-border-radius: 0.5em;
    -webkit-border-radius: 0.5em;
    background-image: -moz-linear-gradient(top, bottom, from(#090), to(#060), color-stop(25%, #cfc));
    background-image: -webkit-gradient(linear, left top, left bottom, from(#090), to(#060), color-stop(25%, #cfc));
}
div#nav ul li a {
    display: block;
    color: #fff;
    padding: 1em;
}

I discussed gradients towards the end of my last post on CSS3. The main gotcha is that Gecko and WebKit use a different syntax, though it's straightforward to translate between the two. I'm creating a top to bottom gradient from medium green to slightly darker green, but stopping at very light green one quarter of the way down. This is what it looks like:

Links with CSS gradient background

Next I started thinking about whether it was possible to achieve a sliding doors style bevel effect with CSS gradients, by using a gradient with dark edges. My approach was to layer the gradients using alpha transparency to let the bottom one show through, a top to bottom one on the li with a left to right one on the a:

div#nav ul li {
    -moz-border-radius: 0.5em;
    -webkit-border-radius: 0.5em;
    overflow: hidden;
    background-image: -moz-linear-gradient(top, bottom, from(rgba(0,255,0,1)), to(rgba(0,51,0,1)), color-stop(2%, rgba(0,255,0,1)), color-stop(4%, rgba(102,255,102,1)), color-stop(96%, rgba(102,255,102,1)),color-stop(98%, rgba(0,153,0,1)));
    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0,255,0,1)), to(rgba(0,51,0,1)), color-stop(2%, rgba(0,255,0,1)), color-stop(4%, rgba(102,255,102,1)), color-stop(96%, rgba(102,255,102,1)),color-stop(98%, rgba(0,153,0,1)));
}
div#nav ul li a {
    color: #fff;
    display: block;
    padding: 1em;
    -moz-border-radius: 0.5em;
    background-image: -moz-linear-gradient(left, right, from(rgba(0,255,0,0.5)), to(rgba(0,51,0,0.5)), color-stop(2%, rgba(0,255,0,0.5)), color-stop(4%, rgba(102,255,102,0.5)), color-stop(96%, rgba(102,255,102,0.5)),color-stop(98%, rgba(0,153,0,0.5)));
    background-image: -webkit-gradient(linear, left top, right top, from(rgba(0,255,0,0.5)), to(rgba(0,51,0,0.5)), color-stop(2%, rgba(0,255,0,0.5)), color-stop(4%, rgba(102,255,102,0.5)), color-stop(96%, rgba(102,255,102,0.5)),color-stop(98%, rgba(0,153,0,0.5)));
}

Here is the end result:

Links with overlaid CSS gradient backgrounds for bevel effect

As you can see, it's not entirely effective thanks to the scaling issue I pointed out above. The width of the element is linked to the apparent depth of the bevel as you can only set the colour stops as percentages. For my test case of a collection of different width buttons this makes it look a bit funny, where it might work better is if you have a set of equal size buttons which scale according to font size - then the bevel wouldn't get less significant as the text got bigger.

Also note that, in CSS3, there's no need to use multiple elements in order to get multiple background images as a single element can have multiple backgrounds defined in its CSS rule. In this case it doesn't really matter, because the nature of a menu means I had two elements overlaid anyway, but I can get exactly the same effect as above by using the following CSS instead:

div#nav ul li {
    -moz-border-radius: 0.5em;
    -webkit-border-radius: 0.5em;
    overflow: hidden;
}
div#nav ul li a {
    color: #fff;
    display: block;
    padding: 1em;
    -moz-border-radius: 0.5em;
    background-image: -moz-linear-gradient(left, right, from(rgba(0,255,0,0.5)), to(rgba(0,51,0,0.5)), color-stop(2%, rgba(0,255,0,0.5)), color-stop(4%, rgba(102,255,102,0.5)), color-stop(96%, rgba(102,255,102,0.5)),color-stop(98%, rgba(0,153,0,0.5))),
                      -moz-linear-gradient(top, bottom, from(rgba(0,255,0,1)), to(rgba(0,51,0,1)), color-stop(2%, rgba(0,255,0,1)), color-stop(4%, rgba(102,255,102,1)), color-stop(96%, rgba(102,255,102,1)),color-stop(98%, rgba(0,153,0,1)));
    background-image: -webkit-gradient(linear, left top, right top, from(rgba(0,255,0,0.5)), to(rgba(0,51,0,0.5)), color-stop(2%, rgba(0,255,0,0.5)), color-stop(4%, rgba(102,255,102,0.5)), color-stop(96%, rgba(102,255,102,0.5)),color-stop(98%, rgba(0,153,0,0.5))),
                      -webkit-gradient(linear, left top, left bottom, from(rgba(0,255,0,1)), to(rgba(0,51,0,1)), color-stop(2%, rgba(0,255,0,1)), color-stop(4%, rgba(102,255,102,1)), color-stop(96%, rgba(102,255,102,1)),color-stop(98%, rgba(0,153,0,1)));
}

Simply list the images top to bottom in a comma separated list, so I simply took the gradient from the li rule above (the 'underneath' element) and added it after the a background.

At this point I gave up on the bevelled buttons and started thinking about some other 3D style effects that could be achieved. I went for some shiny, metallic style buttons with pseudo 3D indented text - the sort of thing you'd normally do in Photoshop:

div#nav ul li {
    -moz-border-radius: 0.5em;
    -webkit-border-radius: 0.5em;
    background-image: -moz-linear-gradient(top, bottom, from(#090), to(#060), color-stop(25%, #cfc));
    background-image: -webkit-gradient(linear, left top, left bottom, from(#090), to(#060), color-stop(25%, #cfc));
}
div#nav ul li a {
    color: rgba(0,51,0,0.5);
    text-decoration: none;
    font-weight: bold;
    display: block;
    padding: 1em;
    -moz-border-radius: 0.5em;
    text-shadow: 1px 1px rgba(204,255,204,0.5), -1px -1px rgba(51,102,51,0.5);
}

As you can see the gradient is the same as in the example above, but I've used multiple text shadows, one offset slightly above and left, the other below and right, to fake the 3D look:

Links with CSS gradient background and text shadow for 3D effect

This gives me an opportunity to do a declarative interactive effect with the hover rule. Reverse the text shadows and, because this will produce an apparent one pixel shift in the position of the text, translate the item to counter that:

div#nav ul li a:hover {
    text-shadow: -1px -1px rgba(204,255,204,0.5), 1px 1px rgba(51,102,51,0.5);
    -moz-transform:  translate(-1px, -1px);
    -webkit-transform:  translate(-1px, -1px);
}

Here's what one of the buttons looks like with the mouse pointer over:

Links with CSS gradient background and text shadow for 3D effect in hover mode

It occurred to me that it would be possible to invert the apparent 3D of the whole button rather than just the text:

div#nav ul li:hover {
    -moz-border-radius: 0.5em;
    -webkit-border-radius: 0.5em;
    background-image: -moz-linear-gradient(bottom, top, from(#090), to(#060), color-stop(25%, #cfc));
    background-image: -webkit-gradient(linear, left bottom, left top, from(#090), to(#060), color-stop(25%, #cfc));
}
div#nav ul li a:hover {
    text-shadow: -1px -1px rgba(204,255,204,0.5), 1px 1px rgba(51,102,51,0.5);
    -moz-transform:  translate(-1px, -1px);
    -webkit-transform:  translate(-1px, -1px);
}

Of course, I'm not a graphic designer, so when I say reverse I really mean I just flipped it upside down, but I'm sure someone with some artistic skills could make it work properly. Here's what mine looks like:

Links with CSS gradient background and text shadow for 3D effect in hover mode on both link and containing element

So, on the subject of my lack of graphic design skills, I wondered if someone who had some skills had attempted this sort of thing. Google presented me with GirlieMac's CSS3 Gradients: No Image Aqua Button. Although this is pure CSS and uses no images, it does add additional markup. I started with her CSS but tried to apply it to my existing markup. Instead of adding an extra element for the glare I tried to achieve a similar effect with a radial gradient:

div#nav ul li {
    -webkit-border-radius: 16px;
    -moz-border-radius: 16px;
    border: 2px solid #ccc;
    position: relative;
    font-family: Lucida Sans, Helvetica, sans-serif;
    font-weight: 800;
    color: #fff;
    text-shadow: rgba(10, 10, 10, 0.5) 1px 2px 2px;
    text-align: center;
    vertical-align: middle;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    background-color: rgba(60, 132, 198, 0.8);
    background-image: -moz-linear-gradient(0% 0%, 0% 100%, from(rgba(28, 91, 155, 0.8)), to(rgba(108, 191, 255, .9)));
    background-image: -webkit-gradient(linear, 0% 0%, 0% 90%, from(rgba(28, 91, 155, 0.8)), to(rgba(108, 191, 255, .9)));
    border-top-color: #8ba2c1;
    border-right-color: #5890bf;
    border-bottom-color: #4f93ca;
    border-left-color: #768fa5;
    -webkit-box-shadow: rgba(66, 140, 240, 0.5) 0px 10px 16px;
    -moz-box-shadow: rgba(66, 140, 240, 0.5) 0px 10px 16px; /* FF 3.5+ */
}
div#nav ul li a {
    display: block;
    color: #fff;
    text-decoration: none;
    padding: 10px 32px 6px;
    -webkit-border-radius: 8px;
    -moz-border-radius: 8px;
    background-color: rgba(255, 255, 255, 0.25);
    background-repeat: no-repeat;
    background-image: -moz-radial-gradient(center top, 5px, center top, 50px, from(rgba(255, 255, 255, 0.7)), to(rgba(255, 255, 255, 0)));
    background-image: -webkit-gradient(radial, 50% 0%, 5, 50% 0%, 50, from(rgba(255, 255, 255, 0.7)), to(rgba(255, 255, 255, 0)));
}

The radial gradient for the glare was the toughest thing as I couldn't find a way to make it anything other than round. There doesn't seem to be a way within the definition and applying background size to it didn't seem to work. In the end I settled for positioning it so the middle of the gradient was aligned with the top border:

Sort of Aqua buttons with no extra markup

That concludes my experiments with buttons, here's a page with all the button examples above, you need Firefox 3.6 or later or a recent Chrome/Safari to see everything working. As per usual there's more to say, but that will have to wait for (yet) another post &amp;#59;&amp;#41;


Tweet this!
Send feedback »PermalinkPermalink

26/09/09

02:46:50 am Permalink Adventures in Web 3.0: Part 3 - More CSS 3

Categories: Front End Web Development, Standards, HTML and CSS

I talked about CSS3 in the last post in this semi-regular series on the web standards of the future, but there are some new features supported in the latest Chrome release and Firefox alpha which make this worth a second post. This time I'm going to focus on background sizing, CSS gradients and RGBA colours.

To start with, scaling background images with the background-size rule. I started off with the example code from the MDC article on -moz-background-size and tried to make a background image that covers the entire page:

body {
    background-image: url('bg-image-1.jpg');
    background-size: 100% 100%;
    -moz-background-size: 100% 100%;           /* Gecko 1.9.2 (Firefox 3.6) */
    -o-background-size: 100% 100%;             /* Opera 9.5 */
    -webkit-background-size: 100% 100%;        /* Safari 3.0 */
    -khtml-background-size: 100% 100%;         /* Konqueror 3.5.4 */
    -moz-border-image: url('bg-image-1.jpg') 0;/* Gecko 1.9.1 (Firefox 3.5) */
}

This results in a page which looks like this:

Screenshot of scaled background image in Firefox

As you can see above, there is a way to support scaled backgrounds in Firefox 3.5 by using -moz-border-image. A while back I'd read a comment which indicated that border image ought to be able to achieve this sort of thing but, when I tried to understand the spec, I couldn't figure it out. The example above shows it's a lot less complex than I thought back then, unfortunately it doesn't work that well for this particular use case as the border-image inherits from body to html. This is not as weird as it might seem, there's specific discussion in the CSS spec of the inheritance relationship between the two - read the third and fourth paragraphs of this section. Unfortunately, you end up with a repeated background, even if you explicitly set the background and border images to none. The workaround for this, if you want to support Firefox 3.5, is to make sure there's no margins, padding or border on body in which case the repeated background will be covered up. The alternative, which I will follow for most of the rest of my examples, is not to worry about supporting Firefox 3.5.

A second problem is that the background image only covers the area taken up by the body, if your browser window is longer than the body then the image repeats down the page. For certain images this may be what you want, in this case I wanted it to cover the whole background. This is easy to fix by adding background-attachment: fixed; to the CSS to arrive at my second example. This is what it looks like in Firefox with that change:

Screenshot of scaled background image in Firefox

In Chromium, the scaled background image generally seems to be better quality - there is an image-rendering property but it didn't seem to make much difference.

Screenshot of scaled background image in Chromium

It also works in Opera 10, but only on Windows, and it doesn't scale to the full page height, and looks only marginally better than browsers with no background scaling:

Screenshot of scaled background image in Opera 10

The other problem with background-attachment: fixed; is, if your page is longer than a single screen, it noticeably impacts performance as you scroll up and down the page and the browser is forced to re-render everything.

So, having successfully scaled a background image across the entire page my thoughts turned to the page content, now almost unreadable. Since this is the sort of arty design which is all about the background image we don't want to cover it up completely, but we do want to provide enough contrast to read by, so some sort of semi-transparent background is in order. There are all sorts of ways to do this already, and several of them can be made to work across browser (and so would be the ones to use in any 'real' website, of course). But rather than have an alpha transparent PNG as a background, or use CSS transparency on the back one of a pair of nested elements, let's use RGBA colours to set a semi-transparent background colour:

h1, p {
    background-color: rgba(255,255,255,0.5);
}

This specifies values for red, green and blue respectively as numbers between 0 and 255 and then a number between 0 and 1 for the transparency. So bright red would be rgba(255,0,0,1). This does make the text a bit more readable while leaving the background visible:

Screenshot of RGBA backgrounds Firefox

But perhaps, in the interests of exploration, we should relax this pesky 'readable' requirement and see what other effects can be achieved with CSS 3. One interesting possibility is the new gradient property introduced by WebKit and now also supported in the next version of Firefox. This allows you to produce some pretty crazy effects without any images at all:

Screenshot of gradient backgrounds Firefox

The bottom paragraph is the one I started with and it's fairly straightforward, here's the code for it:

p {
    background-color: transparent;
    background-image: -moz-linear-gradient(top left, bottom left, from(rgba(255,255,255,0.9)), to(rgba(255,255,255,0)));
    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,0.9)), to(rgba(255,255,255,0)));
}

Unfortunately there are two different syntaxes. In Firefox/Gecko the gradient type is part of the rule name - -moz-linear-gradient - whereas in WebKit the type is the first parameter (the alternative to linear is radial). There's no W3C spec at the moment, so it's possible neither option will be the same as the standard. I also found that Chromium didn't work too well with top left though I don't think the order should make any difference. In both cases, what is specified is a linear gradient, from top left to bottom left starting at white with 90% opacity, ending at white with 0% opacity (ie. transparent). Note that, because I was inheriting a background-color of 50% opacity white on the p elements, I had to override that in this rule to make the bottom of the gradient fully transparent. This could cause some problems in practical scenarios, as you may want there to be a background colour where there is no gradient support and the gradient otherwise. An approach which works in Firefox is to use a background property instead of background-image, Firefox 3.5 then displays the default inherited RGBA background and ignores the whole rule with the gradient:

background: -moz-linear-gradient(top left, bottom left, from(rgba(255,255,255,0.9)), to(rgba(255,255,255,0))) transparent;

Of course, you could achieve the gradient effect with a tiled PNG background image, with background-size you could even make it stretch to fill behind arbitrary content, so why would you bother? There are two reasons:

  1. There is no need to download an additional file from the server, so pages should load (slightly) faster
  2. The gradient is recalculated when the browser is zoomed, so it will always be smooth, no image resizing artefacts

So, now we're into the spirit of things, let's try something a little more complex. The next gradient I added a colour as well as a transparency and also tried to make at an angle by going from top left to bottom right:

p {
    background-image: -moz-linear-gradient(top left, bottom right, from(green), to(rgba(0,255,0,0)));
    background-image: -webkit-gradient(linear, left top, right bottom, from(green), to(rgba(0,255,0,0)));
}

As far as I could tell, this made no difference - the gradient still went straight across the image:

Screenshot of linear gradient from top left to bottom right Firefox

Since the browsers seem to be ignoring it anyway, I removed the top and bottom. In Firefox this made no difference, however in Chromium it stopped the gradient showing at all. So although you have to specify both directions in Chromium, it always ignores at least one of them. If two match then they will be ignored, if they're all different then it's not clear which way round your gradient will be - try it and see.

With CSS gradients you're not limited to heading smoothly from a single starting colour to a single ending colour, you can also visit any colour you want in between with a color-stop:

p {
    background-image: -moz-linear-gradient(left, right, from(rgba(255,0,0,0.8)), to(rgba(128,0,255,0.8)), color-stop(16%, rgba(255,165,0,0.7)), color-stop(32%, rgba(255,255,0,0.6)), color-stop(48%, rgba(0,128,0,0.5)), color-stop(60%, rgba(0,0,255,0.6)), color-stop(76%, rgba(75,0,130,0.7)));
    background-image: -webkit-gradient(linear, left top, right top, from(red), to(violet), color-stop(16%, rgba(255,165,0,0.7)), color-stop(32%, rgba(255,255,0,0.6)), color-stop(48%, rgba(0,128,0,0.5)), color-stop(60%, rgba(0,0,255,0.6)), color-stop(76%, rgba(75,0,130,0.7)));
}

This final example, which I adapted from the MDC page, paints a rainbow in the background. You can have an unlimited number of colour stops in your gradients, simply specify a percentage and a colour. If you specify 25% and blue then you gradient will pass through blue a quarter of the way along.

This post has been in gestation for over two months and I find myself well over my thousand word target for a post and only halfway through all the stuff I wanted to cover, so there will be at least one further post on CSS3. Having covered the basics of scaled backgrounds and gradients in this post, I'm aiming for some practical ways to use these features in web pages of the future.


Tweet this!
1 feedback »PermalinkPermalink

04/09/09

12:38:03 am Permalink Arbitrary Element Rotation in IE - the Matrix Filter

Categories: Front End Web Development, Standards, HTML and CSS

There was a post on WebDesign-L today asking about angled text, which reminded me of the excellent Text Rotation with CSS a few months back on snook.ca. Jonathan focussed on the IE only BasicImage filter to provide an alternative to the (also proprietary but future standard) -webkit-transform and -moz-transform properties to rotate text. That's fine as far as it goes, but BasicImage can only rotate in increments of 90°. Jonathan mentions the alternative, the Matrix filter, but says "the coordinates still don't make any sense to me."

Now, WebKit and Gecko also support matrix transformations, so I'm thinking if I can only work out what the co-ordinates mean we might have the basis of a more general purpose solution. So I looked at the Wikipedia examples of linear transformation matrices and then the Mathamazement introduction linked to from the MDC page. If you can hear a loud whooshing sound right now, that's the echo of a load of math stuff flying completely over my head...

However, all was not lost - if we go back to the MSDN Matrix transform article you'll see there's some example code. With something to hack around I thought I had a much better chance of figuring out how things worked. I started off with the fourth sample, it uses Javascript to animate the Matrix transform, rotating and expanding a div element.

I modified the code a bit, but I'll take you through the key points before I show you what I finished up with. First of all, the Matrix transform needs to be defined on the element in question before you can script it:

<DIV ID="oDiv" STYLE="position:absolute;
filter:progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand')"
		onfilterchange="fnSpin(this)" >

The animation is powered by fnSpin which calls two subsidiary functions, fnResize to resize the image and fnSetRotation to rotate it. Here's the original code for the rotation:

//oObj input requires that a matrix filter be applied. 
//deg input defines the requested angle of rotation.
var deg2radians = Math.PI * 2 / 360;
function fnSetRotation(oObj, deg)
{    rad = deg * deg2radians ;
    costheta = Math.cos(rad);
    sintheta = Math.sin(rad);

    oObj.filters.item(0).M11 = costheta;
    oObj.filters.item(0).M12 = -sintheta;
    oObj.filters.item(0).M21 = sintheta;
    oObj.filters.item(0).M22 = costheta;

}

So here's it all nicely laid out with all the hard math stuff already done for us. The basic math stuff is it's converting the degrees into radians, then using sine and cosine functions to calculate the four matrix values. So now I'm thinking I can reverse engineer this function to work out what the matrix values will be for any angle. I started off with modifying the fnSetRotation so that the matrix values were visible on the page - adding four text inputs and these lines:

    document.getElementById("fM11").value = oObj.filters.item(0).M11;
    document.getElementById("fM12").value = oObj.filters.item(0).M12;
    document.getElementById("fM21").value = oObj.filters.item(0).M21;
    document.getElementById("fM22").value = oObj.filters.item(0).M22;

Then I added a button which rotates the element to 45° when clicked (warning, IE only):

Rotate element by 45 degrees in IE

The next step was to make the page work cross browser, accept a value for the input number of degrees and, for bonus credits, generate the relevant CSS for copy and pasting. First of all I cleaned up the output a bit with toFixed method as, when the values got close to zero, they end up in exponent format rather than just rounding to 0. This gave me numbers I could plug straight into a CSS template, for example here's the declaration to rotate an element by 45°:

filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);

So now I thought it would be a fairly straightforward task of plugging these values into the Mozilla format:

-moz-transform:  matrix(a, b, c, d, tx, ty)

The tx and ty are for translation, so I just set them to zero, then plugged M11, M12, M21 and M22 into a, b, c and d respectively. Unfortunately, it didn't work! The element was reflected in respect to IE in Firefox for certain rotations.

Someone who actually understands what's going on with the matrices could probably explain what's happening (probably I have the co-ordinate system the wrong way up?) but I fell back on trial and error and eventually decided that swapping b and c worked. See my final matrix calculator here - input an angle in degrees, click the button and see the CSS code for achieving it in IE, Gecko and WebKit.

Some things I noticed but didn't have time to investigate fully:

  • IE seems to rotate around the center, Firefox and Chrome seem to rotate from top left. There's a -moz-transform-origin and equivalent WebKit property to control things in those browsers, not sure what equivalents IE offers.
  • Other CSS declarations are applied after the transform in IE, while they seem to be applied before the transform in Firefox and Chrome (ie. if you add 200px padding to the top of an element, Firefox will rotate around a point 200px above the 'start' of the element, IE will apply 200px padding after the transform).
  • Using the rotate syntax in Gecko and WebKit is a lot simpler, but if you want to rotate stuff in IE too using a similar format in all three might be more simple in the long run? Especially if you want to script it.

Tweet this!
10 feedbacks »PermalinkPermalink

02/09/09

06:21:22 pm Permalink Rounded Corners for Image Elements in Firefox with SVG

Categories: Front End Web Development, Standards, HTML and CSS

In my post on CSS3 a few months back I mentioned some issues I had getting -moz-border-radius to clip content that it contained. After my writing the post the question came up a couple of times on stackoverflow, first from Brandon Wang and later a similar one from dougoftheabaci. Here is a simple example of the problem, the CSS is uncomplicated:

.target {
    border-radius: 20px;
    -moz-border-radius: 20px;
    -webkit-border-radius: 20px;
}

With that class applied direct to an img element it looks great in Safari and Chrome:

Example page in Chrome

But it doesn't work so well in Firefox:

Example page in Firefox

Today I came across the Applying SVG effects to HTML content article on the MDC wiki, one of the things it discusses is using SVG as a clipping mask for HTML elements. It occurred to me that it ought to be relatively straightforward to use a clipping mask to create the rounded corner effect directly on an img element in Firefox.

So I started off with the example code from the article, removed the circle, made the rectangle full size and used the rx and ry attributes to round the corners:

<svg:svg height="0">
    <svg:clipPath id="c1" clipPathUnits="objectBoundingBox">
        <svg:rect x="0" y="0" rx="0.05" ry="0.05" width="1" height="1"/>
    </svg:clipPath>
</svg:svg>

Then use a CSS class to apply it to my image:

.target { clip-path: url(#c1); }

It didn't work at first, but then I realised I'd created my document with a .html extension rather than .xhtml - to allow mixing of SVG into XHTML the document must have content type application/xhtml+xml. Once I fixed this the effect was exactly as I'd hoped:

Example SVG page in Firefox

Here is the full code. Of course, embedding it all this way isn't going to work for any browser other than Firefox 3.5, and serving application/xhtml+xml is going to break Internet Explorer, so it's not really a practical solution in it's current state. It's more useful to break the content into separate files, then the SVG can be served as image/svg+xml while your HTML is text/html. This final example works across browsers and will round the corners of the image in Chrome, Safari and Firefox.


Tweet this!
5 feedbacks »PermalinkPermalink