No Mod Required

Archive for the 'standards' Category

A New Site I Made is Live and I Have (nearly) Achieved CSS Nirvana

I recently did CSS/HTML and WordPress theming for Tom's My Card. My Work. blog. Normally, I would get all that excited about a WordPress theme, since I've done several, but this one had a lot going for it from a CSS perspective. I'm actually really excited about it.

What's so special about this one, you ask?

Well, for starters, there's only one style sheet (1!) and in that one style sheet there's a total of 0 (zero) rules included for a specific browser. It's true. Actually, as the last step before I published it out to his server I deleted the lines in my template to pull in an IE6 or IE7 sheet. They were empty :) Across everything else? It just works. No hacks. No weird code. No nothing. Just one style sheet that works in pretty much every major (and minor) browser. I mean it, too- I tested the home page* in Browsercam and it only really fails on browsers that I don't care about at all- IE5.2 on the Mac, Netscape 4.78, IE5.0 and IE5.5, etc.

I have seen the future**, and it is one style sheet with no hacks.

Hallelujah.

In fact, when coupled with another technique I used for the first time in this layout (using overflow:auto to clear floats) and the fact that it's fully em-based and scales like a dream, this layout is probably the cleanest I've ever done- at least in terms of CSS.

Some time soon I'm going to clean the markup up further to get it in line with the level of the CSS in terms of cleanliness (I didn't fully craft every line in the theme, using some default markup in places,) re-skin it and publish it out as a theme for folks to download. That'll be pretty sweet.

Beyond that, which is going to take a little while so I've got to do something to tide you all over, I'm going to detail some of the techniques at play in the layout later on this weekend, starting with a head-slapping, *doh* filled post about the magical powers granted by my newfound knowledge of overflow:auto.

overflow:auto!

*Bugs will shake out in both the home page and other pages. I know this.

**Frighteningly it fails in IE8 beta, but then again it is a beta (fingers crossed)

Internet Explorer 8 Is a Day Old and I'm Already (kinda) Frustrated.

I'm kidding of course, my overall initial reaction is actually quite positive. For instance, I just saw it do this:

ie-8-acid2-test.jpg

That promises good things in my future :)

Still, looking at my site, I'm presented with my very first Internet Explorer 8.0 Mystery ™

I use opacity to do the image replacement in my logo. As the following image shows, neither of the two techniques I'm familiar with to set opacity work in IE8:

opacity-fails.jpg

The Internet Explorer filter:


#logo a {
filter:Alpha(opacity=0)
}

and the CSS3 Color Module color property:


#logo a {
opacity:0
}

both fail.

The Google tells me nothing and searching MSDN also turns up nothing, so I'm left to grapple with my first "what the?!?" of the IE8 Era…

Where oh where has my opacity gone?
Where on Earth can it be?

I'm going to keep at it, since I do use opacity often enough to turn this into an official "issue" for me. To that point, if anyone out there has any idea what's up with opacity and IE8, I'd love to hear about it.

Acid3 Test Released. I Took Some Screen Captures. Lots of Fail.

Screen Shots

These were done with browsercam, so the test may or may not have fully run its course by the time the 15 second delay was up. Still, it's not like any of these browsers were close, so a few seconds isn't going to save them if, in fact, they were cut off before the rendering was over.

As a note, I tried to do the Capture in Konqueror and it pretty much made Browsercam cry and/or vomit ("Do NOT Try The Capture Again!") I'm going to log in shortly to see what the actual damage is.

[update]It's a known bug[/update]

Reference Rendering:

reference-rendering.png

And now the Fail parade:

Camino 1.51 Mac OS 10.5

camino-151-mac.png

Firefox 2 Mac OS 10.5

firefox-2-mac-105.png

Firefox 3 Mac OS 10.5

firefox-3-mac.png

Internet Explorer 6 Windows XP

ie-6-xp.png

Internet Explorer 7 Windows Vista

ie-7-vista.png

Internet Explorer 7 Windows XP

ie-7-xp.png

Firefox 2 Windows XP

firefox-2-xp.png

Firefox 3 Windows XP

firefox-3-xp.png

Safari 3 Mac OS 10.5

[update]as seen in the comments the latest build is now scoring 87/100[/update]

safari-3-105.png

[update]Sorry Opera! I had the capture, but forgot to post it in my rush to get out the door today[/update]

Opera 9.24 Windows Vista

opera-924-vista.png

Here's Konqueror built from SVN 2008.03.06 (thanks to Ronald Hummelink and everyone else that sent in Konqueror captures)

acid3_konqueror_20080306.png

And here's IE8 Beta:

ie8-acid3.png

Opera 9.5:

acid3-opera-95.jpg

Firefox 3 Beta 4 on Windows XP

firefox-3-beta-4-acid3.jpg

[update! 3-27-2008] As everyone now knows, we've had a winner in the race to pass Acid3. Congrats to the WebKit team

acid-3-passed.jpg

[update 2008.9.02 I added Google Chrome Screen shot]

Now, the press release:

Acid3: Putting Browser Makers on Notice, Again. - The Web Standards Project

Acid3: Putting Browser Makers on Notice, Again.

Released: 3 March 2008 | Author: The Web Standards Project

The Web Standards Project (WaSP) today announced the release of Acid3, the latest in a line of tests designed to expose flaws in the implementation of mature Web standards in Web browsers. By making sure their software adheres to the test, the creators of these products can be more confident that their software will display and function with Web pages correctly both now and with Web pages of the future.

The Acid3 Test is designed to test specifications for Web 2.0, and exposes potential flaws in implementations of the public ECMAScript 262 and W3C Document Object Model 2 standards. Collectively known as DOM Scripting, it is these technologies that enable advanced page interactivity and power many advanced web applications such as web-based email and online office applications.

As a series of 100 mini-tests, Acid3 has already been found to expose flaws in all tested browsers, including Internet Explorer, Firefox, Opera, and Safari. WaSP hopes that Acid3 will prove useful to browser makers during the development of future versions of their products.

WaSP has a history of such initiatives. In 1997, emeritus member Todd Fahrner, together with a group of crack Web developers dubbed the “CSS Samurai,” created an “Acid Test” that highlighted shortcomings in browser support for CSS. The Acid Test was instrumental in moving the industry much closer to the goal of consistent rendering of Web pages in different browsers. This was followed by Acid2 in 2005, designed to expose flaws in the implementation of mature Web standards such as HTML, CSS, and PNG. Acid3 builds on and extends this legacy to web applications in 2008.

Acid3 can be found online at http://www.webstandards.org/acid3/

JSON Feeds For Fun and Profit Part 3- wherein Eval() kind of bums me out

(and several months later I finish my little JSON series…)

So far my exploration of JSON has been a fun-filled walk in the park. Moonbeams and rainbows. All that.

This last post on the subject is slightly less cool as I get into one of the least attractive components of the whole JSON thing- the use of eval() to transform a text response into a proper JavaScript object. The use of eval() is one of the reasons I originally was a little shy about using JSON. Why? eval() is slow and I try to stay away from slow if at all possible. That and the idea of eval()-ing code from some third party makes me wary.

Obviously, if you're using a callback, or are just using a built in object reference which inserts itself into memory as soon as the script is attached a script to a page, eval() won't come up, but sometimes there's no other option but to ingest the JSON feed as plain text and eval() it into Object-hood. The feed I was helping out a co-worker with last week (AKA back in August when I originally wrote this) which spurred on this very series of posts was one such feed. It looked something like this example cribbed from JSON.org:

{
    "glossary": {
        "title": "example glossary",
		"GlossDiv": {
            "title": "S",
			"GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
					"SortAs": "SGML",
					"GlossTerm": "Standard Generalized Markup Language",
					"Acronym": "SGML",
					"Abbrev": "ISO 8879:1986",
					"GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
						"GlossSeeAlso": ["GML", "XML"]
                    },
					"GlossSee": "markup"
                }
            }
        }
    }
}

As you can see, if you attach that script to the document using a script tag, there's absolutely no way to get anything out of it. No amount of poking at it will communicate the contents of that file to the rest of the scripts on the page. There's no reference in memory and there's no way to create one. It has to be ingested using an XMLHttpRequest and eval() has to bring it into life. Since it uses XHR and not a dynamic script tag, that means a local pass-though script on the server side is needed to get around cross site scripting constraints. In other words, we're looking at a big pile of "why bother?" For my money, if you have to go through these steps, then plain old XML makes more sense.

Anyway, if you have to use JSON and the only option is a response structured like the above, then the following is one way to handle it.

First, an example "tag cloud" hacked together with a raw JSON tag feed from del.icio.us:

Now some code:

<script type="text/javascript">
function ajaj(json_doc,callback) {
  //ajaJ? get it? so clever.
  //create a cross browser XMLHttpRequest object
  //let's do this
  	http= new function() {
  	var xmlhttp;
  	try {
  	//start with the standard
 	 xmlhttp = new XMLHttpRequest();
 	}
 	catch(e) 
  	{
 	 //do the IE thing if the above fails
	  //IE deserves some props since they actually created 
 	 //the whole XMLHttp thing
  	xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  } 
  return xmlhttp;
  };
  //is it okay to try this?
  if((http.readyState==0)||(http.readyState==4))
  {
  //I do this in a try/catch since  there's 
  //always a chance of some 
  //sort of data error and I want the 
  //possibility of using a fallback function
  //if there is some sort of internet hiccup. 
  //I also hate showing errors to the user
  //In this case I could just drop it since
  //I'm not doing anything special here, but 
  //I'm not about to reinvent the wheel
  //since this is more about JSON 
  //than the way I use XMLHttpRequest 
  try 
  {
  //open the connection
  http.open("GET", json_doc, true);
  //set up a function to handle the response
  http.onreadystatechange = function handle_http_response() 
  {
  //I take no chances.
  //you can have have readyState of 4, a status 
  //of 200 and still run into trouble
  //I want to know all my ducks are in a row :) 
//I'm all belt and suspenders 
  if ((http.readyState == 4)&&(http.status==200)&&(http.responseText!=null)) 
  { 
//First I create an empty object, the same structure as the regular Delicious JSON feed
//I like the structure of their feeds so I'm replicating them here.
  Delicious = {}
  //Use eval to bring the response into the Object
//Again, I'm using Delicious' own Delicious.tags structure
  Delicious.tags=eval('('+http.responseText+')');
  //we've got this handy object now that we've done our eval
  //we pass it as an argument to the callback function
  callback(Delicious);
  }
  }
  }
  catch(e) 
  {
  //we could do some stuff here to handle errors
  }
  //let's put an end to this transaction
  http.send(null);
  }
  }
  //this is the callback function
  function delicious_me(Delicious) {
  //this is an object functioning as 
  //an associative array
  //not a proper indexed array typical of most JavaScript
  //so we use a for... in loop
  //which loops through all the members of the array by named reference
  for (var i in Delicious.tags) {
  //inside the loop i will refer to the property name
  //tags[i] will return the value
  //BTW, the tag cloud is a total hack. 
  //I should do some better math here, 
  //but like the XHR stuff above 
   //this isn't really about a tag cloud...
  document.getElementById("delicious").innerHTML+="<a href='http://del.icio.us/rob_react/"+i+"' style='font-size:"+Delicious.tags[i]/2+"px;line-height:"+Delicious.tags[i]/2+"px;'>"+i+"</a>\n";
  } 
  }

The interesting bit, at least in the scope of this article, is this where we use the eval method to pull the responseText into the Delicious and pass it to the callback function.

Delicious.tags=eval('('+http.responseText+')');
callback(Delicious);

Nothing groundbreaking there and, honestly, not much for me to get excited about. I just don't like the use of eval and would go to great lengths to avoid using it. That said, if you're in a situation where a raw JSON feed is all you've got to work with, this is a way out of the jam.

For my next, and final entry in this I'll use this handy JSON parser for the safe alternative to eval(). That should make me a little happier than this example.

A Three Column CSS Layout Using Just an Unordered List

Why? For fun, of course- and to expand on a point I made today at work…

Some background- I'm working with a new guy who is not so savvy to modern HTML/CSS layout techniques and today, while talking to him about some potential techniques he might use for an upcoming site, I remarked - "With CSS you can make practically any element do whatever you need it to do, so don't always assume you need to wrap things in a DIV* to make a layout work." The point related to the dangers of DIV-itis and my desire** to pare down markup to its absolute (meaningful) minimum. But it also illustrates a basic idea that, once fully understood, opens up all sorts of options for problem-solving.

So, anyway, chewing on the exchange on my way home from work I was struck with the idea of doing a simple three column layout using nothing but an Unordered List. My original comment was about a UL used as a menu and tweaking it to behave like a bunch of divs (which was his original idea for the menu) or something, so I thought it might be interesting to go all out use a UL for the entire layout, tweaking the LIs in a whole bunch of different ways to suit my needs. If that doesn't illustrate my point, nothing will.

And, yeah. I'm sure a bunch of people have already done this. It was still interesting for me to do so it was well worth the effort no matter how many people out there are yawning in my general direction.

And here it is, in all it's List-y glory- A Three Column CSS Layout Using Just an Unordered List

Here's the pertinent markup

<ul id="container">
<li id="header">header</li>
<li id="content">
<h1>Content</h1>
<p>Lorem ipsum dolor sit amet... Pellentesque in erat.</p>
</li>
<li id="right-sidebar">right sidebar</li>
<li id="left-sidebar">left sidebar</li>
<li id="footer">footer</li>
</ul>

And here's the CSS

* {
	margin:0;
	padding:0;
}
li {
	list-style-type:none;
}
#container {
	margin: auto;
	width: 825px;
	border: thin solid #FF0000;
	position:relative;
}
#container #header {
	padding: 25px;
	height: 50px;
	width: 775px;
	border: thin dashed #999999;
}
#container #content {
	padding: 10px;
	width: 400px;
	margin-left: 200px;
	border: thin solid #0066CC;
}
#container #footer {
	height: 100px;
	border: thin solid #663300;
}
#container #left-sidebar,
#container #right-sidebar {
	border: thin solid #FF9900;
	position: absolute;
	width: 200px;
	top: 105px;
}
#container #left-sidebar {
	left: 0px;
}
#container #right-sidebar {
	right:0;
}

The sidebars are absolutely positioned- a technique I use because (a) I hate sidebars that stretch further than the content column and (b) I like to be able to push content further up in the source order so as to feed that stuff to search engines first. If it was an issue and one or both of the sidebars needed to drive the height and source order didn't matter I could tweak it to satisfy those requirements. That said, this is my party so I'm going with the absolutely positioned sidebars :) Other than that, it should be pretty self-explanatory to anyone familiar with modern layout techniques. UL id="container" takes the place of the traditional "wrapper" type DIV, the LIs take the place of the other section DIVs one might expect to find in any site that uses modern techniques and none of the actual CSS is all that different than what one might expect with a regular DIV based layout.

I know what you're thinking- why is this even useful? To be honest, probably not. DIV's are fine for this job. Separating sections of a site are what they're designed for, so there's no shame in using them for this purpose.

[devil's advocate]
There might be less "extraneous" markup with this technique since the UL is necessary as part of the list structure and the traditional div id="container" or "wrapper" is only there for the purposes of layout. Also, the connection between the sibling LIs might be stronger semantically than the connection between the adjacent DIVs which don't have any unifying structure. Other than those incredibly geeky points I'm not convinced there's much to this beyond the thought experiment value of it all. Which is valuable. I'm just not sure I'm going to actually build any sites with this technique any time soon.

Or maybe I will.
[/devil's advocate]

Shout in the comments if I stopped making sense at any point in this post. I started yawning about thirty minutes ago so I can't be sure of anything I've written in that time :)

*which isn't to say I'm not down with DIV's. I'm on the DIV train, I just know from painful experience that valid, table-less code with millions of nested DIVs can be just as miserable as any old school, nested table beast.
**which translates to the company's desire since I'm driving the "how" of our front end practice

I Hate HTML Emails… But I'm Still Responsible for Them, So This Is Cool.

I like the sound of the Email Standards Project

The Email Standards Project works with email client developers and the design community to improve web standards support and accessibility in email.

Our goal is to help designers understand why web standards are so important for email, while working with email client developers to ensure that emails render consistently. This is a community effort to improve the email experience for both designers and readers alike.

I don't do HTML emails very often any more (although they still sneak through,) but we still do a ton of them at Cramer so just for my co-workers' sake I'd love to see email clients come together the same way browser vendors have (finally) come together in order to allow us to code clean, light, standards compliant HTML emails. If I never had to rely again on the dirty tricks we use to get HTML emails to render I would be a happy man. It would also save clients money since the unwieldy beasts we send out are a lot more difficult to maintain and edit than something with nice structure would be.

Did you know- 8 Bit PNG transparency is just like an old school transparent Gif

Don't believe me? Check it out:

There's nothing fancy going on there at all. Here's the full document:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Untitled Document</title>
  <style type="text/css">
  <!--
  #png {
  background: url(sample-bg.jpg);
  height: 200px;
  width: 200px;
  }
  -->
  </style>
  </head>

<body>
  <div id="png">
  <p><img src="png-sample.png" alt="PNG Sample text" width="94" height="50" /></p>
  <p><img src="png-sample.png" alt="PNG Sample text" width="94" height="50" /></p>
  <p><img src="png-sample.png" alt="PNG Sample text" width="94" height="50" /></p>
  </div>
  </body>
  </html>

As you can see, I'm just grabbing the images and dumping them into the doc. This fails in IE4 and Netscape 4.78. It works in everything else Browsercam offers.

This is different than 32 Bit PNG transparency (24 Bit color depth with 8 Bit alpha,) but still a useful thing to know if, like me, you're transitioning over to PNGs from GIFs for web graphics.

JSON Feeds For Fun and Profit Part 2 - Callbacks with Twitter

In my first exploration of the JSON data interchange format, I used it in its most basic way possible. I attached a script (from delicious) to a page and simply used the built-in object created by their implementation to generate an unordered list of my recent del.icio.us posts. While it showed how easy it is to use the O, a handy, structured, Javascript Object, provided by JSON, it didn't really illustrate how to manipulate the page with new data after page load. That is, after all, a very now thing to do, so it's kind of important. This article will examine how to dynamically load a JSON feed and how to get data out of Twitter and into a usable form using a callback.

While one can load JSON using XMLHttpRequests, the real benefit of JSON is to be able to load data from a third party without worrying about the browser security model and without having to rely on a local pass through file. So, we're going to load the data without XHR in this example. Instead we're going to load it using dynamic script tags.

Here's the function I'm using in my example:

function twitter_me() {
   var twitter_JSON = document.createElement("script");
   twitter_JSON.type="text/javascript"
   twitter_JSON.src="http://twitter.com/statuses/user_timeline/rob_react.json?callback=twitter_callback&count=10"
   document.getElementsByTagName("head")[0].appendChild(twitter_JSON);
}

First I create a new script element in the document, then I set the type and source. The source is the interesting part. As you can see, after the URL for the JSON document representing my Twitter updates, I append a query string. The first argument for that query string (callback=twitter_callback) is where the magic happens. There I'm defining the function that will be called when the script is attached to the document. To see what happens compare the first few characters of the JSON script called with and without the callback.

Here it is, as referenced in the above code

twitter_callback([{"source":"web","text":"426 queries in 107.66 milliseconds. drupal is teh awesome",

and called without the callback (but with the count, so the data structure returned is the same)

[{"source":"web","text":"426 queries in 107.66 milliseconds. drupal is teh awesome",

Notice the function referenced in the first example. Right there, as soon as the JSON document is loaded, JavaScript executing the callback function with the JSON object as an argument. I'm a JavaScript geek and all, so I'm biased... but that's just plain cool.

So what happens next, you ask? Well, since I defined a function called twitter_callback, that function is run and I'm able to do all sorts of cool things with the provided object. Here's the function in all its glory:


function twitter_callback(twit) {

 //this is the div I'm writing the content to
 var t_div = document.getElementById("twitter");

//these are some other variables, mostly
 //placeholders so that the code is a little clearer
 var who,what,when,icon, bgcolor

//start the ul
 t_div.innerHTML = "<ul>"

   //loop through the twit object
   for (i=0;i<twit.length;i++) {

   //Look at me use the JavaScript modulus operator to do even/odd rows.
   if(i % 2) {
      bgcolor="#efefef"
   } else {
      bgcolor="#ddd"
   }

   //Right here, I've broken out the separate bits of the string into placeholder variables
   //so you can more easily see the dot notation and array indices in place
   //once you figure out the structure it's dead easy to reference data in an Object
   icon=twit[i].user.profile_image_url;
   who=twit[i].user.name;
   what=twit[i].text;
   when=twit[i].created_at.substr(0,19);

   //and here I mash it all up into a fancy li
   t_div.innerHTML +="<li style='background:"+bgcolor+" url("+icon+") no-repeat'><strong>"+who+"</strong>: "+what+" ("+when+" GMT) </li>"
   }
  //and close the UL
  t_div.innerHTML += "</ul>"
}

Here it is in action (be patient, Twitter can be slow):

Call me crazy, but that's something like 75% away from being a widget and there are no cross domain concerns to deal with so it could easily be spread far and wide. There's a lot to like right there :)

That's it for this time. Feel free to ping me with questions or comments.

Next up, as I mentioned, one can load JSON via XMLHttpRequest, so I'll be building an example that does just that. There's a wrinkle tossed in with that technique that will make things, if not tougher, a little less cool (eval(), I'm looking in your direction.) More on that when I put it all together.

Display: inline-block is the bee's knees. CSS 2.1

I was looking at the CSS 2.1 Candidate Recommendation today and notices that there's a New 'display' value- 'inline-block'. That caught my eye as I'm long actually familiar with the inline-block display value. I've used it a couple of times for IE-specific browser hacks and I've always thought it would be cool if it were picked up by Firefox. Hopefully now that it's made its way into a candidate recommendation and, presumably a real specification, they'll add support and then I'll be happy- in a really nerdy way.

Oh… you want to know why?

Basically inline-block does what it sounds like it does. It's an inline element (it's displayed within a containing block and flows in the same line) that behaves like a block (which in practical terms means it can have explicit height, width, padding and margins, etc.) The one place I would use it all the time is in the all-too common "logo inline in a corporate footer" situation. Here are a couple of examples of ones I've implemented recently, so you know of what I speak.

From MSLifeLines – Offering multiple sclerosis (MS) education & support:

mslifelinescom.gif

From EForAllExpo.com:

eforall.gif

As you can imagine they're both implemented with an image and a link. In the case of the EForAll site, something like this:

An <a href="http://www.idgworldexpo.com/"><img src="/themes/idg_assets/images/idg_world_expo.gif" alt="IDG World Expo"></a> Event

With inline-block, I could instead do something like this and I would have a nice computer (or human with a screen reader) sentence instead of some text with an image rammed in the middle obscuring the actual meaning:

An <a href="http://www.idgworldexpo.com/" id="idg-link" >IDG World Expo</a> Event

Here's the CSS:


#idg-link {
background:url(idg_world_expo.gif) no-repeat;
display:inline-block;
width:150px;
height:60px;
text-indent:-9999px;
}

It's a very typical image replacement, but with inline-block you can have an image replaced anchor behave just like an image. That's useful to me. I approve.

Here's a sample. IE6/7, Safari 1.2+ and Opera 9.+ should see this work (with some variation in Opera.) Firefox 2 and below? Not so much. As reported in the comments this now works in Firefox 3

CSS Attribute Selectors. I'd like to be able to use them.

I'm looking something like two or three years into the future to a point where I can implement a small, almost neglible upgrade to the way I serve back, next and home buttons for my galleries. Add one part coding neuroses, one part browser incompatibility and one part CSS and this article will be a small glimpse at the way I think.

This will be fun.

In my gallery pages I have something similar to the the following HTML markup on every page:

<div id="gallerynav">
  <a href="previous image link" title="Previous image" rel="prev" id="prev">Prev</a> 
  <a href="main gallery link" title="Up to the Main Gallery Page" rel="toc" id="toc">ToC</a> 
  <a href="next image link" title="Next gallery image" rel="next" id="next">Next</a>
</div>

That's styled with the following CSS

That's styled with the following CSS
  #gallerynav a {
  display:block;
  width:21px;
  height:18px;
  position:absolute;
  text-indent:-5000px;
  }
  a#prev:hover {
  background:url(http://media.drunkenfist.com/img/back_o.gif) no-repeat;
  } 
  a#toc:hover {
  background:url(http://media.drunkenfist.com/img/home_o.gif) no-repeat;
  } 
  a#next:hover {
  background:url(http://media.drunkenfist.com/img/next_o.gif) no-repeat;
  } 
  #prev {
  background:url(http://media.drunkenfist.com/img/back.gif) no-repeat;
  left:25px;
  } 
  #toc {
  background:url(http://media.drunkenfist.com/img/home.gif) no-repeat;
  left:50px;
  } 
  #next {
  background:url(http://media.drunkenfist.com/img/next.gif) no-repeat;
  left:75px;
  } 

Functionally it's what I want- descriptive text, replaced with small graphics*, controlled by reasonably descriptive, compact markup. There's just one thing that I'd love to be able to clean up and can't because Internet Explorer 6 doesn't understand CSS Attribute selectors. IE7 does. So it's just a matter of time before I can start using them. Which is why I spend any time thinking about this at all…

What are CSS Attribute selectors, you ask?

The spec sayeth:

CSS2 allows authors to specify rules that match attributes defined in the source document.
5.8.1 Matching attributes and attribute values

Attribute selectors may match in four ways:

[att]
Match when the element sets the "att" attribute, whatever the value of the attribute.
[att=val]
Match when the element's "att" attribute value is exactly "val".
[att~=val]
Match when the element's "att" attribute value is a space-separated list of "words", one of which is exactly "val". If this selector is used, the words in the value must not contain spaces (since they are separated by spaces).
[att|=val]
Match when the element's "att" attribute value is a hyphen-separated list of "words", beginning with "val". The match always starts at the beginning of the attribute value. This is primarily intended to allow language subcode matches (e.g., the "lang" attribute in HTML) as described in RFC 1766 ([RFC1766]).

What does that all mean? From a W3c perspective, the rel attribute is a flexible method to add semantic data to links. Google uses it to great effect with the rel="nofollow" attribute value pair.

From my perspective it means I could dump the ids on all of those anchor tags and use the following CSS instead for the same visual effect:

a[rel="prev"]:hover {
background:url(http://media.drunkenfist.com/img/back_o.gif) no-repeat;
} 
a[rel="toc"]:hover {
background:url(http://media.drunkenfist.com/img/home_o.gif) no-repeat;
} 
a[rel="next"]:hover {
background:url(http://media.drunkenfist.com/img/next_o.gif) no-repeat;
} 
a[rel="prev"] {
background:url(http://media.drunkenfist.com/img/back.gif) no-repeat;
left:25px;
} 
a[rel="toc"] {
background:url(http://media.drunkenfist.com/img/home.gif) no-repeat;
left:50px;
} 
a[rel="next"] {
background:url(http://media.drunkenfist.com/img/next.gif) no-repeat;
left:75px;
} 

Why bother? Well, if I could use the above code, I could drop the IDs and just use the rel attribute. Since I'm already using the rel attribute in that markup** I don't really need the IDs for any real reason. I'm just using them as a hook for styles. Which is really no reason at all :)

Here's a sample. IE6 will display a blank iframe.

For the real minimalist approach (at least in terms of markup) I could always use Adjacent Sibling Selectors. In that case I could drop both the ID and REL and just rely on source order to style the links from first to last. Of course, IE6 doesn't support this either. IE7 does though, so if the adoption rate ever spikes***, expect to see code like this coming to a browser near you.


 #gallerynav a:hover {
/*this is the first A in #gallerynav*/
background:url(http://media.drunkenfist.com/img/back_o.gif) no-repeat;
}
 #gallerynav a + a:hover {
/*this is the second A in #gallerynav*/ 
background:url(http://media.drunkenfist.com/img/home_o.gif) no-repeat;
} 
#gallerynav a +a + a:hover {
/*this is the third A in #gallerynav*/
background:url(http://media.drunkenfist.com/img/next_o.gif) no-repeat;
} 
#gallerynav a {
background:url(http://media.drunkenfist.com/img/back.gif) no-repeat;
left:25px;
} 
#gallerynav a +a{
background:url(http://media.drunkenfist.com/img/home.gif) no-repeat;
left:50px;
} 
#gallerynav a + a + a{
background:url(http://media.drunkenfist.com/img/next.gif) no-repeat;
left:75px;
} 
 

Here's a sample. IE6 will display just the first link.

*one thing I'd like to do is use a single graphic and just change the background position. Cutting down on calls to the server- it's the new black.
**because it adds metadata to the links that could potentially be an aid to user agents or to myself when coding JavaScript
***I'm not holding my breath.