Google Blogoscoped

Friday, March 30, 2007

CSS Tips

1. Color shortcuts and color conversions

In CSS, you can write color values in a variety of ways, e.g. "RGB(0,0,0)", "black", and "#000000" all result in black. However, you can also use a shortcut for hexadecimal values that follows this form: "#000". #0BC for example means #00BBCC but is a little quicker to write.

Sometimes when you need to feed color values into forms, e.g. for tools such as Google AdSense where you can define a border and background color, you need to provide hexadecimal values only. However, as you may want to mirror an existing RGB value in your CSS file, you need a color converter. Some years ago I created desktop tool WebCol to convert between the different CSS color formats and I'm still using it for those occasions... and there are also online converters.

2. CSS bug hunting

Your stylesheet doesn't seem to do what you want it to do? There's a variety of approaches you can use to debug the problem now:

3. Media separation

One bonus of using external CSS files instead of inline stylesheets is that you can serve different layouts to different media – like a mobile layout vs a screen layout. So you'll be using something like...

<link rel="stylesheet" href="default.css" type="text/css" media="screen,projection,print" />
<link rel="stylesheet" href="mobile.css" type="text/css" media="handheld" />

(If you don't add "projection" to the default media mix, Opera users who switch to full-screen mode are in for a surprise – a page deprived of layout!)

But whooopsie – this won't even work as intended in some mobile browsers (hint: Microsoft)! Oh well, it still makes sense to use external CSS files, because it's easier to maintain and allows for neat caching of files.

On a side-note, I'm always using my (old) free Netpadd so I don't have to manually type lines like the "link" stuff above (I just type "<link " and the rest can be selected from an auto-complete box... I can then hit F9 to jump straight to the CSS file, or Ctrl+F9 to jump to the JS in a new Netpadd window). Netpadd, which basically looks like Notepad, also has CSS auto-completion, so I can type "bor" (for "border"), hit Ctrl+F5, and have a CSS reference pop up.

4. Center the thing

You'll often want to have a "page"- or "content"-class div to center everything. One way to do so is to use "margin-left: auto; margin-right: auto; position: relative". You can even use absolutely positioned layers inside this div.

5. Anti-aliased PNG files

I was in for a surprise recently when I checked the CSS code for Google's new personalized homepage themes... the logo was using cross-browser anti-alias for a varied background! As you probably know, anti-alias is giving rough pixel lines a softer border by blending in some pixels with the background. However, with GIF files this only worked on a background of a static color, because you have to decide beforehand into which color you want to blend those pixels. In theory, PNG files do support varied levels of transparency; however, an Internet Explorer 6 bug prevents this from working cross-browser (and when it doesn't work, we call it "ghosting" – take a very close look at some of the Picasa Web Albums buttons and you'll spot it).

But back to Google – here's what I saw in their CSS (slightly reformatted for readability):

#regular_logo
{
    background:url('test.png'); width:150px; height:55px;
}
/* \ */
* html #regular_logo
{
    background:none;
    float:left;
    width:150px;
    filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='test.png', sizingMethod='scale');
}
/* */

So instead of outputting the image in the HTML, they use a (semantically slightly incorrect) empty div which then receives a background PNG file. That PNG file includes varied transparency (it softly blends into the background, including some shadow effect). Now in the CSS they use two selectors, a "normal" one and one that is shielded by some hack that serves some browsers only. And you can see there's a line starting with "filter:progid" which does the magic of fixing the IE6 PNG bug.

6. Quick 'n' dirty rounded box corners

If you want to have a very quick way to give something rounded corners, and you don't really care if the effect is lost on some browsers, you can use a definition like the following: "-moz-border-radius: 8px". It's a Firefox thing that's playing around with CSS3, but it only takes a couple of seconds to write, and if the effect is not important to the overall layout – and you don't mind that the borders aren't anti-aliased – I think it's better than nothing (for example, I use it at the "Game Over" dialog of this game, where I don't consider the rounded borders crucial in any way).

7. Opacity

To make a div layer (or mostly anything else) blend in with the background, you can use the following property-value pairs:

-moz-opacity: .5;
filter: alpha(opacity=50);

The first is for Firefox, the second for Internet Explorer. This effect can also create neat shadows – just use an empty shadow layer (that is, a div of the class "shadow"), give it a slight offset, a black background, and put it behind another box.

PS: You want to access a "moz" value in JavaScript? Use something like this (note the upper-case "m" and "o"):

var elm = document.getElementById("myElm");
myElm.style.MozOpacity = .5;

Update: You can drop the "-moz-" prefix in all modern versions of Firefox. [Thanks Peter Gasston!]

8. Cross-browser padding

In the CSS box model, "padding" is the distance from a box edge to its inner stuff, whereas "margin" is the distance to stuff outside the box. Now, when you give something a specific width, say "200px" (200 pixels), and you add a padding of "10px", this means that the box has a width of 220 pixels (200 + 10 at the left side and + 10 at the right side). Probably not the most intuitive, and the programmers of older versions of Internet Explorers got it wrong and will not add the padding to the overall box size (so in IE5, the width is 200 pixels, even if there's a 10 pixel padding).

So if you need very precise box dimensions, one solution is to use a browser hack. It increases the maintenance efforts for your file, so use CSS hacks sparingly, and only when really needed. We can use this hack:

.navigation
{
    padding-left: 10px;
    padding-right: 10px;
}

.navigation
{
    width: 200px;
}

.navi\gation
{
    width: 220px;
}

The first selector is for all browsers and defines the basic box. The second selector is for conforming browsers who we hope to get the padding/ width calculation right. And the third selector is for older versions of Internet Explorer which we "hope" get the calculation wrong (per the W3C guidelines), so we help out a little by using a backslash preceding a selector letter that's in the range from g-z.

9. CSS hacks

A CSS hack basically means you're trying to "split" a portion of your stylesheet to target a specific (e.g. broken!) browser. There are many of these hacks around – maybe you want to target IE5+ with something, or maybe you want to target everything but Firefox 2, and so on. One way to search for new hacks is to go to Google Code Search and use the query lang:css hack. You'll quickly find some inspiration. But don't let the dark side of the force corrupt you, Luke...

10. Moving layers

When you add animation to your page, and you don't go for Flash, you're typically using DHTML – the combination of HTML, JavaScript and CSS. One way I find to be neat for handling moving div layers – which can wrap anything, like animated sprites, a help monologue, and what-not – is to use a Sprite class in your JavaScript file. Yep, JavaScript allows you to create objects, only that they're calling it prototypes.

Now, your sprite object will mirror some of the values that exist in the world of CSS; for example, the sprite will have x and y properties (the "left" and "right" of the CSS). Other values, like speedX or speedY (the object's velocity), or something more exotic like "mood" (the object's current behavior pattern, e.g. "aggressively hunting player sprite" vs "calmly moving towards the edge of the screen"), can be unique to your specific sprite class. You can then use a sprite array in your JavaScript file, let each object of this array "grab" its element – using document.getElementById("sprite" + i) – and create a global setTimeout event to iterate over all sprites every few microseconds.

Here's some excerpted code to illustrate this with a living example from this game (also check out this type of sprite object JS in other games). Basically I just needed to have a little king walk around at the bottom of the game, from left to right, showing different animation cells so it looks like he's moving his feet:

var g_avatars = new Array();

....

function initAvatars()
{
    var i = 0;
    g_avatars[i] = new Avatar();
    g_avatars[i].x = 60;
    g_avatars[i].speedX = 6;
    g_avatars[i].y = 500;
    g_avatars[i].type = 1;
    g_avatars[i].limitX2 = 415;
}

...

function moveAvatars()
{
    for (var i = 0; i < g_avatars.length; i++) {
        g_avatars[i].speedX *= .8;
        g_avatars[i].move();
        g_avatars[i].render();
    }
    setTimeout("moveAvatars()", g_avatarsDelay);
}

...

Avatar.prototype.move = function()
{
    var isMoving = this.speedX != 0 || this.speedY != 0;
    if ( isMoving || getRandomInt(0, 100) <= 10 ) {
        if (--this.cellCounter <= 0) {
            this.cellCounter = 5;
            if (++this.cell > 2) {
                this.cell = 1;
            }
        }
    }

    this.x += this.speedX;
    this.y += this.speedY;

    if (this.x > this.limitX2) {
        this.x = this.limitX2;
    }
    else if (this.x < this.limitX1) {
        this.x = this.limitX1;
    }
}

function Avatar()
{
    this.x = 0;
    this.y = 0;
    this.speedX = 0;
    this.speedY = 0;
    this.cell = 1;
    this.cellCounter = 0;
    this.type = 1;
    this.limitX1 = 80;
    this.limitX2 = 1000;
    this.elm = document.getElementById("avatar" + gAvatarId++);
}

Advertisement

 
Blog  |  Forum     more >> Archive | Feed | Google's blogs | About
Advertisement

 

This site unofficially covers Google™ and more with some rights reserved. Join our forum!