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

12/06/13

12:16:00 am Permalink Update: Features for Font Fanatics

Categories: Standards, HTML and CSS

Some time ago I wrote a blog post Firefox 4.0 Features for Font Fanatics. Since that time the spec has evolved and support has arrived in Chrome and IE10 so I thought it was worth an update.
Here's the example CSS rule from the original post:

.example {
    font-feature-settings: "dlig=1,tnum=1,ss01=1";
}

This is what the syntax now looks like:

.example {
    font-feature-settings: "dlig" 1, "tnum" 1, "ss01" 1;
}

As you can see the change is subtle. For practical support you'll need to use vendor prefixes, of course:

.example {
    -moz-font-feature-settings: "dlig" 1, "tnum" 1, "ss01" 1;
    -webkit-font-feature-settings: "dlig" 1, "tnum" 1, "ss01" 1;
    -ms-font-feature-settings: "dlig" 1, "tnum" 1, "ss01" 1;
}

I've updated my examples to use the new syntax:


Tweet this!
Send feedback »PermalinkPermalink

20/04/11

01:16:00 am Permalink IE10 and the Future of CSS Layout

Categories: Front End Web Development, Standards, HTML and CSS, Blogging and Internet Culture

Last week the first developer preview of IE10 was released. Among several experimental features included were the first Microsoft implementations of CSS3 Flexible Box Layout Module and CSS3 Grid Alignment. These are possibly the most exciting things to be added to CSS since drop shadows...

First up the flexible box layout module, or flexboxes. This has already been implemented in Firefox and WebKit, but that version of flexboxes isn't very intuitive, and the draft has since seen a lot of updates. Interestingly, IE10PR1 implements the same version of the spec as Firefox and WebKit but with one important addition: multi-line flexboxes. For me the multi-line properties are what make flexboxes worthwhile for layout, otherwise nearly everything you might currently want to achieve can be done just as easily (and with better backwards compatibility) with display: table-cell.

This is the sort of thing that multi-line flexboxes are useful for, it's a layout typical of shopping and photo gallery sites:

A multiline flexbox layout at 800 pixels width

Here's what my markup looks like, nothing more complex than a list with sixty items in it:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    ...
    <li>59</li>
    <li>60</li>
</ul>

And here's the CSS, the key things to look out for are the display: -ms-box and the -ms-box-lines:

body {
    width: 90%;
    margin: 0 5%;
}
ul {
    display: -ms-box;
    -ms-box-lines: multiple;
    list-style: none;
    width: auto;
    padding: 10px;
    border: 4px dashed #000;
}
li {
    display: block;
    -ms-box-flex: 1;
    padding: 1em;
    margin: 0.5em;
    min-width: 3em;
    border: 4px dashed #000;
}

You may be thinking that's not a hard thing to pull off, so let me show you the same page, in an 800 pixel wide browser above, at 640 and 480 pixels:

A multiline flexbox layout at 640 pixels width

A multiline flexbox layout at 480 pixels width

The number of cells across adjusts to match the width available, but, because these are flexboxes, the width of the elements themselves also adjust so that they always exactly fit the available width. This is unique among our current alternatives:

  • If you were using floats or inline blocks then you'd have to set each element to a fixed width, meaning the container would have to be a fixed width so that the elements could fill it exactly. You'd have to use media queries to assign different fixed widths to the container to change the items per row according to screen resolution.
  • If you were using a layout table or display: table-cell then, although the elements would expand to fit exactly, the number of items per row would be fixed by the markup.

However, nothing is perfect. My multi-line flexbox example has a total of 60 elements, and I picked this number because it is an exact multiple of 2, 3, 4, 5 and 6 (it's the expansion of them if I've got my maths terms right&amp;#58;&amp;#63;&amp;#58;) - meaning that the number of elements will fill the grid whether there's 2, 3, 4, 5 or 6 elements per row. If there's a less perfect number, say 24, then the grid will look a little strange on the last row:

A multiline flexbox layout at 640 pixels width with only 24 elements

I think the box-align property should help with this, but I couldn't make it work in IE10PR1.

So, multi-line flexboxes could be a useful addition to the CSS toolbox when everyone gets round to upgrading to IE10, but, cool as they are, that's not the coolest experimental CSS layout implementation in IE10PR1. There have been a few grid and template based CSS proposals over the years but, apart from an incomplete JavaScript library for CSS Template Layout, none of them have ever been implemented in a major desktop browser. That is until last week, when IE10PR1 implemented Microsoft's own CSS Grid Align proposal from October 2010.

I want you to reflect, now, on all the fun you've had over the last ten years constructing CSS layouts of three equal height columns, with a header and footer, and making them work in IE6. Layouts something like this:

A three column layout with header and footer

Are you remembering all the fun you had? Good, now look at this markup and contemplate how you'd turn it into a three column layout:

<header>Header</header>
<aside class="b">Side bar</aside>
<article>I never am really satisfied...</article>
<aside class="d">Side bar</aside>
<footer>Footer</footer>

Now look at this CSS and start wishing the future would arrive soon:

body {
    width: 90%;
    margin: 0 5%;
    display: -ms-grid;
    -ms-grid-columns: auto minmax(min-content, 1fr) auto;
    -ms-grid-rows: auto minmax(min-content, 1fr) auto;
}
article, aside, header, footer {
    margin: 1em;
    padding: 1em;
    outline: 4px dashed black;
}
header { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 3; }
aside.b { -ms-grid-column: 1; -ms-grid-row: 2; }
article { -ms-grid-column: 2; -ms-grid-row: 2; }
aside.d { -ms-grid-column: 3; -ms-grid-row: 2; }
footer { -ms-grid-column: 1; -ms-grid-row: 3; -ms-grid-column-span: 3; }

That's it, seven declarations is all you need for that three column layout with CSS Grid Align. For this first example I'm going to step through the key points line by line:

display: -ms-grid;

This is the bit which declares we will use the grid layout manager on the children of this element, in the same way that the flexbox layout manager was declared above with display: -ms-box.

-ms-grid-columns: auto minmax(min-content, 1fr) auto;

This line defines three columns, the first and last will shrink to fit the content - that's the default behaviour, similar to a table. The middle column will be a minimum of min-content, which is basically the same as auto, and a maximum of 1fr which is a 'fraction of available space' - quite similar to a flex unit and, since there's only one fractional column, basically equivalent to all the available space.

-ms-grid-rows: auto minmax(min-content, 1fr) auto;

We'll have three rows to go along with our three columns, I won't go over the individual values again. Now the fun stuff:

header { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 3; }

Put the header in the first column of the first row, and make it span three columns. That's it. Really, it's that simple. Let's just do one more:

article { -ms-grid-column: 2; -ms-grid-row: 2; }

Put the article in the second column and the second row. And make me some coffee, white, no sugar. Actually there isn't a CSS property for that yet, maybe CSS4...

But it gets more cool than that. In the example above the order of the elements in the markup matched up with the order they were placed into the grid, but it doesn't have to be that way:

A three column layout with header and footer, with elements placed in a different order

This amazing transformation was achieved without any changes in markup, just messing around with CSS:

header { -ms-grid-column: 1; -ms-grid-row: 2; }
aside.b { -ms-grid-column: 2; -ms-grid-row: 2; }
article { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 3; }
aside.d { -ms-grid-column: 1; -ms-grid-row: 3; -ms-grid-column-span: 3; }
footer { -ms-grid-column: 3; -ms-grid-row: 2; }

In the current draft there's a grid-cell-stacking property which will let you flow multiple elements into a single cell, but in the IE10PR1 implementation things just stack on top of each other:

Multiple elements in cells not working

This is a shame, because the markup is quite straightforward, I won't post all of it because this is already getting quite long (have a look), but here's the nice bit:

article:nth-child(2n+1) { -ms-grid-column: 1; }
article:nth-child(2n) { -ms-grid-column: 2; }

The article elements are assigned to grid cells alternately, unfortunately it doesn't work yet. However you can nest elements set to display: -ms-grid inside each other:

A three column layout with header and footer

Have a look at the source code for that one, it gets pretty hairy, so rather than try and do that sort of thing it's probably best to use some wrapper elements like this:

Grid Align with wrapper elements for cells

Again, view the source code yourself to see how it's put together, but I think these things will be worth re-visiting in a later preview release.

I'm going to finish off with some adaptive layout. Grid Align is a great fit for this because of the complete independence of layout from source order. Here's what I put together based off the previous example at 800 pixel width:

An adaptive Grid Align at 800px width

Now here's the same page at 640 and 480 pixel widths:

An adaptive Grid Align at 640px width

An adaptive Grid Align at 480px width

The markup is, of course, the same in each case:

<header>Header</header>
<div id="sidebar">
    <aside>Side bar 1</aside>
    <aside>Side bar 2</aside>
</div>
<div id="content1">
    <article>Content 1</article>
    <article>Content 3</article>
    <article>Content 5</article>
</div>
<div id="content2">
    <article>Content 2</article>
    <article>Content 4</article>
    <article>Content 6</article>
</div>
<footer>Footer</footer>

By default I've assumed a single column layout:

body {
    width: 90%;
    height: 90%;
    margin: 0 5%;
    display: -ms-grid;
    -ms-grid-rows: auto;
    -ms-grid-columns: 1fr;
}
article, aside, header, footer {
     margin: 1em;
     padding: 1em;
     outline: 4px dashed black;
}
header { -ms-grid-row: 1; }
#sidebar { -ms-grid-row: 3; }
#content1 { -ms-grid-row: 2; }
#content2 { -ms-grid-row: 4; }
footer {  -ms-grid-row: 5; }

Moving on to windows of a minimum width of 600 pixels, move up to two columns:

@media screen and (min-width: 600px) {
    body {
        -ms-grid-columns: auto 1fr;
        -ms-grid-rows: auto 1fr 1fr auto;
    }
    header { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 2; }
    #sidebar { -ms-grid-column: 1; -ms-grid-row: 2; -ms-grid-rowspan: 2; }
    #content1 { -ms-grid-column: 2; -ms-grid-row: 2; }
    #content2 { -ms-grid-column: 2; -ms-grid-row: 3; }
    footer { -ms-grid-column: 1; -ms-grid-row: 4; -ms-grid-column-span: 2; }
}

This is nothing you haven't seen already, but I'll just point out again how cool it is that you can place the elements wherever you want them. Finally, for windows of greater than 760 pixels width, switch back to the three column layout:

@media screen and (min-width: 760px) {
    body {
        -ms-grid-columns: auto 1fr 1fr;
        -ms-grid-rows: auto 1fr auto;
    }
    header { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-column-span: 3; }
    #sidebar { -ms-grid-column: 1; -ms-grid-row: 2; }
    #content1 { -ms-grid-column: 2; -ms-grid-row: 2; }
    #content2 { -ms-grid-column: 3; -ms-grid-row: 2; }
    footer { -ms-grid-column: 1; -ms-grid-row: 3; -ms-grid-column-span: 3; }
}

If you've downloaded the IE10 developer preview have a play round with it yourself, and try not to think about how long you'll have to wait until all this stuff is available in production browsers &amp;#58;&amp;#112;


Tweet this!
Send feedback »PermalinkPermalink

16/12/10

01:58:42 am Permalink Fun with HTML5 Forms

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

There have been many blog posts describing all the new elements and input types HTML5 provides, check out this 24ways post for a good summary, or you could even read chapter 3 of my book. In this post I'm going to focus instead on the validation API and some related HTML5 features by building a simple game based on entering an email address. The goal is to explore some HTML5 features rather than do everything in the most straightforward way, but there should be some practically useful code snippets.

The form itself is going to be very straightforward:

<form id="game">
    <fieldset>
        <legend>Enter a valid email address before the timer runs down</legend>
        <label for="email">Email</label>
        <input id="email" type="email" autofocus required>
    </fieldset>
    <label for="countdown">You have
        <output id="countdown">10</output>
        seconds.
    </label>
    <label for="score">You have
        <output id="score">0</output>
        points.
    </label>
</form>

In the initial version we will take advantage of three HTML5 features:

  • The output element
  • The email input type
  • The checkValidity method from the the HTML5 Form Validation API

In the countdown function we use the value property of the the output element The output element is something like a span, it's a start and end tag with arbitrary content, but you can access the contents using the value attribute like a form field :

var counter = function (cd, sc, em) {
    cd.value -= 1;
    sc.value = calcScore(em.value);
}

This may not look particularly helpful, but consider what the code would look like without the output element (or in a browser which doesn't support output):

var counter = function (cd, sc, em) {
    var count = cd.innerHTML;
	count -= 1;
    cd.innerHTML = count;
	sc.innerHTML = calcScore(em.innerHTML);
}

The function is now mostly about the details of manipulating the DOM, rather than the more straightforward algebraic style of the original. Granted, libraries like jQuery have for a long time provided abstractions which allow you to write this code more cleanly but, like design patterns, these are as much an indication of the lack of expressiveness in the underlying platform as they are examples of best practice.

The game needs a function to calculate a 'score' from the email address. This is, of course, completely arbitrary:


function calcScore(email) {
    var s = 0;
    s += email.length;
    s += email.indexOf('@')>-1?email.indexOf('@'):0;
    s += email.lastIndexOf('.')>-1?email.lastIndexOf('.'):0;
    return s;
}

Now we need to wire up the functions to appropriate events when the game starts. To keep things simple I'm going to store a reference to the interval in a global variable:

var cdInterval;
var gameStart = function () {
    window.clearInterval(cdInterval);
    var em = document.getElementById('email');
    var cd = document.getElementById('countdown');
    var sc = document.getElementById('score');
    cd.value = 10;
    sc.value = 0;
    em.value = '';
    em.readOnly = false;
    cdInterval = window.setInterval(counter,1000, cd, sc, em);
    window.setTimeout(gameOver,10000, cd, sc, em);
}

Again, this function takes advantage of the value attribute on the output elements. The last line calls a gameOver function after ten seconds, we'll use that to clear the interval and calculate the score. The checkValidity method of the email field will be used to determine if the email address is currently valid:

var gameOver = function (cd, sc, em) {
    window.clearInterval(cdInterval);
    var score = calcScore(em.value);
    if (!em.checkValidity()) {
        score = 0;
        window.alert("You lose!");
    }
    cd.value = 0;
    sc.value = score;
    em.readOnly = true;
}

The checkValidity() method is part of the HTML5 validation API, it returns true if the browser understands the contents of the field as a valid email. Note that we are able to call this method without submitting the form, so it is easy to hook form submission into a custom validation function if we want. At no point do we have to implement any code to determine what a valid email address would be. This is a second major saving, have a look at this regular expression example if you thought that code was straightforward:

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

Finally, start the game when the page loads:

window.addEventListener('load', gameStart, false);

The first version of the game will work in Opera, and the development versions of Firefox (4 - relevant bugs are 345624 and 346485 ) and Chrome (9 - relevant bugs are 27452 and 29363). If you want it to work on older versions of Chrome and Safari then you'll need to replace the output based syntax with the innerHTML approach as per the alternate version of counter above.

For the second iteration we're going to look at these two features:

  • Using the pageshow event from the History API
  • How to reset output elements

If the user visits another page after completing the game and then goes back then the game will sit there inert until you reload the page. This is because the game logic hangs off the onload event, and navigating forward and backwards in the browser history doesn't fire onload. The HTML5 History API gives us another option: the onpageshow event. This fires whenever a page is displayed rather than when it is loaded. There is currently support for onpageshow in Firefox and Chrome, but not in Opera, so we'll use the iseventsupported script to detect support and fall back to onload if it's not available:

if (isEventSupported('pageshow')) {
    window.addEventListener('pageshow', gameStart, false);
} else {
    window.addEventListener('load', gameStart, false);
}

We'll also do some extra work in the gameStart function to clear and reinstate the timeout and, since the autofocus attribute gets ignored in onpageshow, focus the input field:

var gameStart = function () {
    window.clearInterval(cdInterval);
    window.clearTimeout(cdTimeout);
    var em = document.getElementById('email');
    var cd = document.getElementById('countdown');
    var sc = document.getElementById('score');
	cd.value = 10;
	sc.value = 0;
	em.value = '';
    em.readOnly = false;
    cdInterval = window.setInterval(counter,1000, cd, sc, em);
    cdTimeout = window.setTimeout(gameOver,10000, cd, sc, em);
    em.focus();
}

Missing from the first version of the game is any way to restart or replay. Let's start off by adding a reset button to the form:

<input id="restart" type="reset" value="New Game">

There are some obvious issues with this, not least that the form reset isn't going to remove the countdown and game over timeouts. However, rather than deal with that by hooking the event in a sensible way, we're going to take this opportunity to investigate an interesting feature of the output element, the defaultValue.

If you try clicking the reset button as it stands, this is what happens to the two output elements:

Picture of the two output elements after a form reset

The output element is something of a hybrid. Although it can be created, declaratively, in markup, it isn't really any use without JavaScript. While not a direct consequence of this state of affairs, it therefore doesn't matter that there's no way to set the default value of an output element in the HTML, you have to do it with JavaScript using the defaultValue property in the DOM. Let's add a function to set up these fields correctly which will be run onload:

var gameSetup = function() {
    document.getElementById('countdown').defaultValue = "10";
    document.getElementById('score').defaultValue = "0";
    document.getElementById('restart').addEventListener('click', gameStart, false);
}

You'll note that there's also an onclick handler added to the button to restart the game. The reason we don't want to capture the form reset event itself is that we're going to use the form reset method within gameStart. These three lines of code:

cd.value = 10;
sc.value = 0;
em.value = '';

Will become this one line of code:

document.getElementById('game').reset();

But now resetting the form will restore the output fields to their defaultValue, as can been seen in the second checkpoint:

Picture of the two output elements after a form reset now that they have defaultValue set

For the third iteration we're going to concentrate on user feedback. This is going to involve both HTML5 and CSS3:

  • Using the onforminput event to update the score immediately
  • Giving instant feedback on the current validity of the email with CSS3

So far feedback to the user happens every second, in the counter function. Instead of updating the the score every second let's update it every time the input changes. There are a number of options in HTML4 already for handling this - we could attach an event to the email field and monitor it for changes, however HTML5 (for now) presents us with another option: onforminput. Instead of being associated with the input field, the item being updated, onforminput can be associated with the output element, the item we want to update:

document.getElementById('score').addEventListener('forminput', updateScore, false);

Because the output element is the target, the update score function looks like this:

var updateScore = function(ev) {
    ev.target.value = calcScore(document.getElementById('email').value);
}

Unfortunately it seems that onforminput may be dropped from HTML5, so we'd better check for support and fall back to capturing any oninput event on the whole form through event bubbling:

if (isEventSupported('forminput')) {
    document.getElementById('score').addEventListener('forminput', updateScore, false);
} else {
    document.getElementById('game').addEventListener('input', updateGlobalScore, false);
}

This does complicate the function to update the score as the target of the event is no longer the output element, it's whatever input element has been updated. At least in our simple case there's only one input element so we can use the ev.target shortcut, just on the other side of the assignment:

var updateGlobalScore = function(ev) {
    document.getElementById('score').value = calcScore(ev.target.value);
}

If you've been following along in Firefox you will have seen that both provide some default styling for invalid fields:

Default invalid field in Firefox

However Chrome (top) and Opera (bottom) don't show any indication:

default invalid field in Chrome

Default invalid field in Opera

Let's apply some styles explicitly to make everything consistent across browsers:

input[type=email]:invalid {
    box-shadow: none;
    background-color: rgba(255,0,0,0.5);
}
input[type=email]:valid {
    box-shadow: none;
    background-color: rgba(0,255,0,0.5);
}

The box-shadow overrides the Firefox default, while the background-color will be applied in all three browsers, as you can see in the third checkpoint.

The game is now functionally complete, but before we wrap this up I'd like to use one further iteration to examine the checkValidity method more closely. Here's what the spec says about checkValidity:

Returns true if the element's value has no validity problems; false otherwise. Fires an invalid event at the element in the latter case.

We only perform additional work so, instead of using the boolean return value of the function to guide or logic, we could make the end of the game event driven. First we'll simplify the gameOver function, as all we need to do there now is ensure the event gets triggered if the email is invalid (and, thanks to onforminput, we know the score will always be up to date):

var gameOver = function (cd, sc, em) {
    window.clearInterval(cdInterval);
    cd.value = 0;
    em.checkValidity();
}

Now we need to declare an function to handle the oninvalidevent, I've chosen to name it in tribute to cheesy European rock:

var theFinalCountdown = function(e) {
    document.getElementById('score').value = 0;
    window.alert("You lose!");
    e.target.readOnly = true;
}

Then we attach it to the oninvalid of the email field:

document.getElementById('email').addEventListener('invalid', theFinalCountdown, false);

Thus concludes my slightly erratic tour through HTML5 form features with the final checkpoint. If you've enjoyed this please check out the more extensive introduction to HTML5 Forms in Chapter 3 of my book.


Tweet this!
Send feedback »PermalinkPermalink

15/11/10

11:22:02 pm Permalink Hello! HTML5 and CSS3 - 50% Off Until 16th December

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

Buy early access to my book Hello! HTML5 and CSS3 and get a 50% discount for the next month with the code html550. Coming soon will be the chapter on HTML5 Forms, which should be pretty useful for getting up to speed in time for the release of Firefox 4.


Tweet this!
Send feedback »PermalinkPermalink

20/07/10

03:17:00 pm Permalink Firefox 4.0 Features for Font Fanatics

Categories: Standards, HTML and CSS

Check out the updated syntax in this newer post.

If you're following the development of CSS3 you may have seen, last June, a proposal from John Daggett of Mozilla to bring advanced typography control to the web. A few months later we saw some experimental Firefox builds which demonstrated the features. The terminology has been through a slight update since then, from font-feature-opentype to font-feature-settings, but last week these experiments finally made their way into the trunk builds of Firefox 4.0 on Windows and Mac.

Even though it doesn't yet work under Linux, I thought it was time to start investigating. In this post I'm going to have a look at what these features can look like, mostly following the examples from the October Mozilla Hacks blog post put using the Calluna Regular font. Then I'll show how to turn them on and off in Firefox with CSS, and find the various features using a font editor.

Open Type Font Features

A font is a collection of glyphs, graphical representations of letters, which are associated with characters. Font features are a scheme for getting alternative glyphs for a given character or sequence of characters. The full list of open type features is available in the spec. Each feature is associated with a four character identifier and can be set to on (1) or off (0), here's an example CSS rule:

.example {
    font-feature-settings: "dlig=1,tnum=1,ss01=1";
}

In the current Firefox 4.0 nightly builds you can enable support for the features below by setting the gfx.font_rendering.harfbuzz.level preference to 1 in about:config.

Ligatures

the characters fb with and without standard ligaturesLigatures are special glyphs which replace runs of commonly occurring characters. There are two levels of ligatures, the standard ligatures are turned on by default in Firefox 4.0. To the left is an example in Calluna. The image on the left is from Firefox 3.6, on the right is Firefox 4.0, you can see the slight different where the two characters connect at the top. the combined glyph is also slightly narrower but that's quite hard to stop.

the characters ffb with and without standard ligatures The second example is slightly more obviously different, though with Calluna being a refined font they're still quite subtle. Again it's Firefox 3.6 on the right and 4.0 on the left, the first 'f' is noticeably smaller than the standard one and there's a single bar across both 'f' characters.

the characters - /> and ->> with and without standard ligaturesThere are some ligatures which don't map to commonly occurring sequences of characters (I know fb might not strike you as particularly common, but there are at least 26 just in English). Calluna has two whole sets of arrow glyphs which replace sequences like '-->>' and '-->', more examples to the right.

So in Firefox 4.0, if the font being used supports standard ligatures, you'll see them. If you want to give directions to Firefox users which will appear like random sequences of dashes and greater thans to everyone else you now can. But that's not all! There's also a more decorative set of combinations called discretionary ligatures. Here's some examples, normal on the left, discretionary ligatures on the right:

  • ct with and without discretionary ligatures
  • qu with and without discretionary ligatures
  • sh with and without discretionary ligatures

The point of all this is, of course, that it can all be controlled through CSS. I've constructed an example sentence which could easily occur in conversation at any time and just so happens to contain plenty of words which show off our ligatures. First, let's see what it looks like with everything disabled:

A sample sentence in Calluna with all ligature features turned off

Here's the code to disable the standard ligatures in Firefox 4.0 using -moz-font-feature-settings:

.alloff {
    -moz-font-feature-settings: "liga=0";
}

The ligature feature is called liga. As I mentioned above, we can set it to 1 to enable (which is the default in Firefox 4.0) and 0 to disable. For comparison let's have a look at the standard Firefox 4.0 rendering:

A sample sentence in Calluna with default ligature features

The CSS for this is straightforward as, of course, there isn't any:

.normal {
    /* it's normal, no setting required...*/
}

Now let's turn on the discretionary ligatures:

A sample sentence in Calluna with all ligature features turned on

The discretionary ligature feature is called dlig, again it's 1 to enable and 0 to disable:

.allon {
    -moz-font-feature-settings: "dlig=1";
}

It could be that, for some reason, you want the discretionary ligatures but not the standard ones:

A sample sentence in Calluna with just discretionary ligature features turned on

Probably not much point with Calluna, but perhaps with a more decorative typeface this would be useful. You can pass several features to -moz-font-feature-settings in a comma separated list:

.discretionary {
    -moz-font-feature-settings: "liga=0,dlig=1";
}

How do you go about finding all these ligatures, standard or discretionary? I used FontForge because it was easily installable on my laptop using yum, the interface is a bit archaic but it seems perfectly functional. Here's what it looks like after I've opened up the Calluna font:

Calluna in FontForge

On the view menu there's a combinations sub-menu upon which the ligatures option lives:

FontForge selecting ligatures from the Combinations sub-menu of the View menu

Now you have a list of ligatures to choose from:

FontForge ligatures window

I opened up the 'ffb' ligature:

Calluna f_f_b glyph in FontForge

Now go to the view menu again and look up the Glyph Info option:

FontForge selecting Glyph Info from the Element menu

In the resulting dialogue you can see that 'ffb' is part of the set of standard ligatures:

Calluna in FontForge, Glyph Info for f_f_b

If instead I open up the 'sk' glyph you can see this is part of the discretionary ligature set:

Calluna in FontForge, Glyph Info for s_k

Numerals

Now that we've had some fun with ligatures you may be wondering what other cool tricks are hidden inside open type fonts. One very practical feature is different styles of numbers. Quick pop quiz time, which is the highest number in the screenshot below?

Numbers in Calluna, default settings

Did you guess it was the second one, or did you have to count carefully? Numbers by default are proportionally spaced, which looks nice when they appear in text but can be misleading when looking at tables of figures. But now, thanks to -moz-font-feature-settings, we have some other options:

Numbers in Calluna, tabular figures

The numbers are now using tabular alignment - each number glyph is an equal width. Tabular numerals use the tnum feature:

.tabular {
    -moz-font-feature-settings: "tnum=1";
}

There are a few other number features. The default numbers in Calluna are 'old style' (onum), you can see there's quite a lot of variance caused by the descenders and ascenders going above and below the line. The alternative to that is 'lining figures' (lnum):

Numbers in Calluna, lining figures

As you can see, they give a much more consistent line shape. And you can also have tabular, lining numerals:

Numbers in Calluna, tabular lining figures

.tabular-lining {
    -moz-font-feature-settings: "lnum=1,tnum=1";
}

Once again you can find all these alternatives in FontForge in the Glyph Info window:

Calluna in FontForge, Glyph info window for 1

Before we finish up with numbers, two more features are worth mentioning. You can control whether or not the zero is 'slashed' with the zero feature:

Calluna in FontForge

The bottom number has -moz-font-feature-settings: "zero=1"; applied. You can also enable fractions with the frac feature setting:

Calluna in FontForge

Stylistic Alternatives

There's plenty more stuff in the open type spec, there are 134 different features listed in the spec I linked to in the introduction. The really fancy bits of fonts are often in the twenty 'stylistic sets' ss01 to ss20. You can find what options you may have in FontForge for a particular font by looking for the display substitutions option on the view menu:

Selecting the view menu in FontForge

You then get a dialogue with a list of the options:

FontForge substitutions dialogoue

From which you can see that Calluna only has glyphs for one of the stylistic sets, and when you select ss01 you'll see that there's not a lot of (regular) characters in that:

Calluna stylstic set 01 in FontForge

And there's more...

There's plenty of other interesting stuff wrapped up in open type font feature settings, we haven't yet discussed swashes, contextual alternates or historical forms for just three examples, but I'll leave exploring those as an exercise for the reader.

Practical Considerations

It may seem slightly superfluous for me to mention this about a CSS feature which, so far, has only an experimental implementation in just one browser, but it would be unwise to build a site which depended on font-feature-opentype, for example by relying on certain ligatures or stylistic sets to convey information. However, for making your typography look a bit nicer for a subset of your users, this stuff will be usable on real web pages from the day of Firefox 4.0's release.

The Future

Now you're salivating for a future where all web browsers support font-feature-settings this is probably a good time to point out that, once that support exists, you'll hardly ever use it &amp;#58;&amp;#41; No, this is not a trick, font-feature-settings itself is only intended to be used for obscure and little used options in the open font features list, the common stuff will be accessible through new values on the font-variant property as well as several font-variant-* properties. The current level of support in Firefox is really only to see how things will look once all that gets implemented.

Let's have a look at how a future you will be recreating my examples with the proposed font-variant property extensions. First, discretionary ligatures:

.discretionary {
    font-variant: additional-ligatures;
}

Alternatively we cold use the specific ligatures property:

.discretionary {
    font-variant-ligatures: additional-ligatures;
}

Each value has a corresponding no-* version, so you could specify no-common-ligatures to turn off the common ones, or no-additional-ligatures in order to override the above rule further down the inheritance chain. What about lining, tabular numerals?

.tabular-lining {
    font-variant-numeric: lining-nums tabular-nums;
}

None of this is implemented yet, so I can't test I've got it right, and all this syntax is still subject to change as the fonts module is still a working draft, so I'll stop there. Hopefully this has given you some ideas for fancy typography to give Firefox 4.0 users a special treat and will set you off on your own investigations.


Tweet this!
1 feedback »PermalinkPermalink

03/03/10

03:28:04 pm Permalink Adventures in Web 3.0: Part 5 - The HTML5 Canvas Element

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

This weekend I came across Prez Jordan's post on Julia sets, I then followed the trail back to his original post on the Mandelbrot set. I've always loved fractals, since I read Gleick's Chaos back in school, and I used to spend hours generating Mandelbrot images on my Amiga when I should have been learning CS at University. I did at one point try to write my own Mandelbrot generator, but quickly got bogged down in trying to manage enough code to get a basic UI in Workbench and gave up and went to the pub.

Anyway, Prez's post stirred my memories and inspired me to once again try to put together my own Mandelbrot generator. Except this time, instead of having to manage OS libraries and indirect addressing in C just to open a window, I would take advantage of the HTML5 Canvas element:

The canvas element provides scripts with a resolution-dependent bitmap canvas, which can be used for rendering graphs, game graphics, or other visual images on the fly.

Basically, canvas creates an area on your web page on which you can then draw lines, curves, images, and text with Javascript. It allows you to do some pretty crazy things, a lot of the more spectacular early HTML5 examples used it.

Adding a canvas to the page is very simple:

<canvas id="mandelbrot" width="320" height="240">A mandelbrot fractal will appear here.</canvas>

The content of the element will only appear if the user agent does not support canvas, it's similar to a noscript block. If your browser does have support, then all the content the user sees will be drawn on with Javascript. You'll notice I specified the width and height, if I left that out it would default to 300 pixels wide by 150 pixels high, which has an interesting side effect I'll discuss below, otherwise, it's much the same as any other element. It doesn't have much in the way of default styling, and it's an inline, rather than block level, element, but you can target it with CSS rules:

canvas { 
    -moz-box-shadow: rgb(0,0,0) 0px 2px 3px 3px; 
    -webkit-box-shadow: rgb(0,0,0) 0px 2px 4px; 
    box-shadow: rgb(0,0,0) 0px 2px 3px 3px;
}

So, we now have an empty rectangle with a nice drop shadow, how do we actually draw something? To draw on a canvas you need to get the context object, which in turn gives you access to all the drawing methods:

var canvas = document.getElementById('mandelbrot');
if (canvas.getContext){
    var ctx = canvas.getContext('2d');
    ctx.fillText('Hello World', 50, 50);
}

To draw a Mandelbrot we need to be able to plot single pixels across the whole element. There isn't a plot method, canvas isn't really intended for pixel by pixel manipulation, but we can pick a colour and plot a one pixel by one pixel rectangle:

var canvas = document.getElementById('mandelbrot');
if (canvas.getContext){
    var x=1, y=1;
    var ctx = canvas.getContext('2d');
    ctx.fillStyle = 'rgb(255,0,0)';
    ctx.fillRect(x,y,1,1);
}

So now you know enough about the canvas element to write a Mandelbrot generator, for the rest we can just steal code off the internet &amp;#58;&amp;#41; Here's what we're aiming for (warning - don't click on the link on a slow computer, it'll take several seconds to render):

An image of the Mandelbrot set, generated in the canvas element with Javascript

Going back to Prez's post, his code is in Python, but it's fairly straightforward looking and easy enough to translate into Javascript. I took the Complex number library from "9.3.6. Example: Complex Numbers" of JavaScript: The Definitive Guide, 5th Edition and then used it to reimplement the Python function:

function mandel(c) {
    var cols = ["rgb(255,0,0)", "rgb(255,165,0)", "rgb(255,255,0)", "rgb(165,255,0)", "rgb(0,255,255)", "rgb(0,165,255)", "rgb(165,0,255)", "rgb(0,0,255)"];
    var z = new Complex(0,0);
    for (var i = 0; i <=20; i++) {
        z = Complex.add(Complex.multiply(z,z), c);
        if (z.magnitude() > 2) {return cols[i % cols.length]}
    }
    return "rgb(0,0,0)";
}

The function returns a different colour based on how many iterations it takes for z to exceed 2 in magnitude when combined with the input value c in the formula z * z + c (if it doesn't exceed 2, then it is in the Mandelbrot set, and so it's black). The input value is the pixel position on our canvas element except translated into a complex number - where the x axis is the real component and the y axis the imaginary one - ctx.fillStyle = mandel(c);. Check the Wikipedia page for full details of how the Mandelbrot set works, I worked out most of the details through trial and error once I had it functional.

I mentioned the default size for a canvas element above, this is 300px by 150px. As an experiment I removed the width and height attributes from the element itself and set the size of the canvas in CSS:

canvas { 
    width: 45%;
    height: 45%;
}

You can view the results here (again, watch out if you have a slow computer). The browser renders the canvas, then scales the results to fit the CSS dimensions. So, if you want your canvas to take up a particular portion of the page (eg. half of it), you need to set the dimensions of the element with Javascript based on the pixel width of the page.

Moving on to Prez's second post, which inspired this whole adventure, where he looks at the Julia set. The Julia calculation is very similar to the Mandelbrot one, except instead of starting with z = 0 like the Mandelbrot it starts with z = t, where t is a complex number. The obvious place to get t is the existing plane of numbers on which we've drawn our Mandelbrot set. A canvas is an element like any other, so simply attach an onclick event to it and then work out the value of t from where the click event fired. Here's the Julia set for real 0.4249, i -0.2666:

An image of a Julia set, generated in the canvas element with Javascript

You can take a look at the final Canvas Mandelbrot/Julia Generator here. In performance terms, doing a calculation to render each individual pixel is a pathologically bad case for canvas - it's not something you'd normally do, you would make use of the higher level drawing controls instead. As it stands it does make an interesting performance comparator between browsers. I tested it in Firefox 3.6 (about 2 seconds to generate each image on my machine), Google Chrome 5 beta (about 1 second for each image) and Opera 10.50 (about half a second for each image), it ought to work in Safari too but I didn't try it. I did a version which used Explorer Canvas to try and see how long IE took, but it kept hitting the script timeout before it was even one third of the way through.


Tweet this!
2 feedbacks »PermalinkPermalink

12/02/10

03:04:05 am Permalink Real World CSS 3

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

I've done a number of posts recently on new features coming in CSS level 3. These posts have mostly been based on features available in the version fours of Safari and Chrome and the Firefox 3.6 release so, while they may be useful if you're producing an iPhone app, they don't seem like they'll be much use on general purpose websites where IE users need to be considered. In this post I'm going to look at how these new CSS features can be made to work, or at least degrade gracefully, in older browsers, including Internet Explorer. I'm going to extend the example from my earlier post on scaling background images and gradients to see what's possible.

So my scaled background image looks OK in Firefox 3.6:

Screenshot of perfectly scaled background image

But it looks a bit crappy in Firefox 3.5, not to mention any other browser which doesn't support background-size:

Screenshot of background image tiling inappropriately

There are going to be some cases where there's nothing you can do, for instance if you want some page elements to be in position over bits of the background, and will just need to take another approach (like putting the image in the page and using a negative z-index). However, in my case, I think that if users can't be treated to a nice full screen photo of palm trees then a tiling 'leafy' image would be an acceptable substitute without ruining the ambience. In order to provide one background image to browser which support sizing and another to those that don't I'm going to make use of another CSS3 feature, multiple background images. In CSS3 compatible browsers you can just provide multiple images in a comma separated list:

background-image: url('1-rhus-typhena-leaf-tile.jpg'), url('bg-image-1.jpg');

All the other background rules accept a similar comma separated list, including background-size. The nice thing about this is the stacking order of the images is defined in the spec:

The first image in the list is the layer closest to the user, the next one is painted behind the first, and so on. The background color, if present, is painted below all of the other layers.

So I put the tiling image 'closest to the user' then in my background-size set it to be zero size. Browsers which support background-size make that image disappear and show the scaled image behind it, those that don't show the tiling image:

background-size: 0% 0%, 100% 100%;
-moz-background-size: 0% 0%, 100% 100%;           /* Gecko 1.9.2 (Firefox 3.6) */
-o-background-size: 0% 0%, 100% 100%;             /* Opera 9.5 */
-webkit-background-size: 0% 0%, 100% 100%;        /* Safari 3.0 */
-khtml-background-size: 0% 0%, 100% 100%;         /* Konqueror 3.5.4 */

Note, the image doesn't actually disappear in WebKit, but if you set background-repeat: no-repeat it's very hard to spot. Opera 10.10 supports background-size (on Windows, anyway) but not multiple background images, which is unfortunate, but things work well on Opera 10.50. So now all we have to worry about is those browsers which don't support background-size or multiple background images. This is easy enough, as they should ignore all the rules they don't understand, so we just need to precede all of the above with a single background-image rule

background-image: url('1-rhus-typhena-leaf-tile.jpg');

Now each browser displays a background image according to their abilities, here's Firefox 3.5 after the fix:

Screenshot of background image tiling with multiple background hack

With one further tweak, we can support IE too. The issue with IE is that it parses the multiple background images even though it doesn't understand them, treating them as a single, invalid background image. For this I resort to IE conditional comments:

<!--[if IE]>
<style type="text/css">
body {
    background-image: url('1-rhus-typhena-leaf-tile.jpg');
}
</style>
<![endif]-->

Screenshot of background image tiling in IE8

Something still needs to be done about the menu examples; to refresh your memory, the first menu example used a single 'button' background image:

Links with scaled background image

This is amenable to the same multiple background hack. This time I used a one pixel wide slice of the button image and repeated it across the background:

background: url('bg-image-3.png') repeat-x;
background: url('bg-image-3.png') no-repeat, url('bg-image-2.png') no-repeat;   /* Image courtesy of http://blog.entropiads.com/2009/01/29/free-3-d-glossy-button-icons/ */
background-size: 0 0, 100% 100%;
-moz-background-size: 0 0, 100% 100%;           /* Gecko 1.9.2 (Firefox 3.6) */
-o-background-size: 0 0, 100% 100%;             /* Opera 9.5 */
-webkit-background-size: 0% 0%, 100% 100%;        /* Safari 3.0 */
-khtml-background-size: 0% 0%, 100% 100%;         /* Konqueror 3.5.4 */

Add some rounded corners and you end up with a reasonable approximation of the original buttons in Firefox 3.5:

Links with tiled background image

The next set of buttons all used gradient backgrounds:

Links with CSS gradient background

I'm just going to look at the first and the last one of these, as all the interesting bits will get covered in these two cases. One thing worth noting at this point - the syntax for specifying gradients has changed significantly in Firefox (and the standard) since the last time I discussed them. It's actually now a lot simpler. Old way:

background-image: -moz-linear-gradient(top, bottom, from(#090), to(#060), color-stop(25%, #cfc));

New way:

background-image: -moz-linear-gradient(top, #090, #cfc 25%, #060);

As you can see, the from, to and color-stop tokens have been removed and now, instead of stating the start and end colours and then any steps in between after that, the colours are now in the order they'll appear. The percentage value is now optional, if you don't specify it then the colour stops will be evenly distributed.

Because this example involves no transparency, supporting browsers which don't understand gradients is easy - just provide a background colour:

background-color: #080;
background-image: -moz-linear-gradient(top, #090, #cfc 25%, #060);
background-image: -webkit-gradient(linear, left top, left bottom, from(#090), to(#060), color-stop(25%, #cfc));

And (for a bit of variety), here's what it looks like in Opera 10.50 Alpha (on Linux):

Links with fallback solid background

You might settle for that in Internet Explorer, but you don't need to! IE has supported gradients since 5.5 thanks to the gradient filter. Here's a simple lighter to darker green gradient in IE8 CSS syntax:

-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr=#FF009900, endColorstr=#FF006600)";

And this is what it looks like in Internet Explorer 8:

Links in IE with CSS gradient background

While you sit there flabbergasted that, once again, IE seems to have been ahead all along in the race to snazzy CSS3 effects, there are a couple of caveats. First off, like all IE filters, underlying this is an ActiveX control, so expect strange issues related to stacking, clicking and (possibly) problems due to the security settings of extremely paranoid Windows admins. Secondly, you can't specify anything other than a flat gradient, there is no equivalent to colour stops. Finally, unlike the 'true' CSS gradients in Firefox and Safari, a filter in IE applies to the entire element instead of just a single property. So while in Firefox you can happily use a gradient in the background-image property and also specify a separate box-shadow property, in IE you have to use all the filters on a single -ms-filter property; this may not be a problem for you, but I've found that combining more than one filter of a different type on a single element can lead to some rather strange results, more on this below.

In my final example in my earlier post, I tried to do some no Image Aqua Buttons without adding additional markup to my fake menu. You may recall one of the issues I had was simulating the 'glare' with a radial gradient because there wasn't a way to make it anything other than round. In the new gradient syntax supported by Firefox, there is now a way to change the shape of the radial gradient:

background-image: -moz-radial-gradient(center 25%, ellipse farthest-side, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0));

You can now specify a shape, circle or ellipse, as well as a number of different 'size constants' - farthest-side in my example above. This allows a lot more flexibility for the radial gradients. Compare the orginal version:

Sort of Aqua buttons with no extra markup

With the new 'ellipse farthest-side' version:

Sort of Aqua buttons now with ellipsoidal glare

Yes, I know, the difference is subtle &amp;#58;&amp;#41;

As I alluded to above, the main issue with these buttons is that we want them to be semi-transparent. Apart from IE, it's been a while since any other major browser didn't support RGBA colour - Opera 9.64 didn't support it, but Firefox 3.0 did. As long as a non-transparent colour is an acceptable fallback we can just rely on normal CSS precedence and specify two background colours:

background-color: rgb(60, 132, 198);
background-color: rgba(60, 132, 198, 0.8);

If you want a completely transparent background in the case where gradients are supported and a coloured background otherwise, you can specify everything in two background rules instead, browsers should ignore the rules they don't understand.:

background: rgb(60, 132, 198);
background: -moz-linear-gradient(left, rgba(28, 91, 155, 0.8), rgba(108, 191, 255, 0.9)) transparent;

Once again for IE we turn to the -ms-filter property. In the earlier gradient example you will have seen startColorstr=#FF009900 - the alpha value is that first hexadecimal pair (the rest make up a standard hex colour declaration), if you set it to something less than FF the colour will be partly transparent. Since I have two elements to use, the li and the a within it, I will attach a gradient filter to both:

div#nav2 ul li {
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#CC1C5B9B, endColorstr=#E56CBFFF)";
}
div#nav2 ul li a {
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr=#88FFFFFF, endColorstr=#00FFFFFF)";
}

The GradientType is 1 for horizontal and 0 for vertical, so the first declaration is a relatively solid dark blue to light blue horizontal gradient and the second declaration is a white to transparent vertical gradient. Finally in IE, because the -ms-filter property is stand-alone, the normal background colour has to be turned off in conditional comments:

<!--[if IE]>
<style type="text/css">
h1, div#nav1 ul li, div#nav2 ul li, div#nav2 ul li a {
    background-color: transparent;
}
</style>
<![endif]-->

This produces some acceptably 'glassy' buttons:

Sort of Aqua buttons in IE 8

In Gecko and WebKit there is also text and box shadow, these can also be emulated by -ms-filter in IE8. It is possible to tack multiple filters on to the declaration like this:

div#nav2 ul li {
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#CC1C5B9B, endColorstr=#E56CBFFF) progid:DXImageTransform.Microsoft.Shadow(color=#3399EE,direction=180,strength=10)";
}
div#nav2 ul li a {
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr=#88FFFFFF, endColorstr=#00FFFFFF) progid:DXImageTransform.Microsoft.Shadow(color=#3399EE,direction=180,strength=10)";
}

The filters are applied in the order they appear in the rule. However, as I alluded to above, this doesn't always have the effect you might expect, here's the result of the above rules:

IE 8 multiple filter fail

Not very useful! You may have better luck mixing other filters, or applying multiple filters of the same type, but in general I would recommend a one filter per element approach. In this case, I think the gradient is more important than the shadow for the general feel, so I'll stick with that.

So now the final 'compatible' version of my example page is ready. For some side by side comparisons, here it is at it's best in Firefox 3.6:

CSS3 Backgrounds example page in Firefox 3.6

This is the same page in Internet Explorer 8:

CSS3 Backgrounds example page in IE 8

And this is the same page again in the legacy Opera 9.64:

CSS3 Backgrounds example page in Firefox 3.6

As you can see, the pages are by no means identical in every browser, but each is displaying the page according to its capabilities, with no need for Javascript hacks and only a little CSS trickery. So, if you are willing to accept that less capable browsers will show less stylish pages (and are willing to write a few redundant CSS rules), it is possible to use several CSS3 features on websites today.


Tweet this!
Send feedback »PermalinkPermalink