Friday, November 03, 2006

Blogger: Condensed Label List  

This is a continuation of the series about customizations I made to my Blogger template after switching to the Blogger beta.

This time I'm going to talk about the condensed list of labels that I've put in the sidebar. Synonyms for "labels" used by other blogging tools include "categories" and "sections". For user navigation I like "sections", personally, so that's what the sidebar says.

Why customize it?

The default list of labels is a standard bulleted list that displays vertically.

The problem with vertical lists is that, well, they're an inefficient use of vertical space in the sidebar. If you have a lot of labels, you'll wind up with an extremely large portion of your sidebar devoted to this list. The vertical space that the list occupies will be far out of proportion to its importance.

Frankly, the list of labels isn't that important to me. It's a nice touch, and I would like them to be there, but I wouldn't say that it needs an entire screenful in my sidebar. So I set out to fix that.

Widget modifications

Here are the modifications I made to the default "Labels" widget to accomplish this.

<b:widget id='Label1' locked='false' title='Sections' type='Label'>
<b:includable id='main'>
  <b:if cond='data:title'>
    <h2><data:title/></h2>
  </b:if>
  <div class='widget-content'>
    <ul style='padding-left:10px; margin:0; text-indent:0;'>
    <b:loop values='data:labels' var='label'>
      <li style='display: inline; list-style-type: none; padding-right: 5px; line-height: 140%;'>
        <b:if cond='data:blog.url == data:label.url'>
          <data:label.name/>&#160;(<data:label.count/>)
        <b:else/>
          <a expr:href='data:label.url'><data:label.name/></a>&#160;(<data:label.count/>)
        </b:if>
      </li>
    </b:loop>
    </ul>
  <b:include name='quickedit'/>
  </div>
</b:includable>
</b:widget>

Widget explanations

Since the styles here are very highly specific to this single list, and unlikely to be re-used, I decided to mix CSS with HTML here by assigning the styles via the style attribute on the tags. This isn't normally good practice, but Blogger templates do it a lot already, and it's actually kind of convenient here.

The display: inline; style on the LI tag makes the list horizontal. Adding list-style-type: none; removes the bullets.

That's almost good enough right there! But during testing I found that the original template used normal spaces in between the label and the numeric post count. Since I am expecting the list to wrap -- it wraps five times at my normal web browser size -- I need it to wrap cleanly. With a normal space, you often get the break in between the label and the post count, which doesn't look right. So I converted the regular space into a non-breaking space (&#160;).

Why '&#160;', rather than something like '&nbsp;'? Because I'm still shooting for XHTML eventually, and XHTML does not recognize '&nbsp;' as an entity. The Unicode code point '&#160;' is the direct equivalent.

After all that was done, padding, margins, and line height were adjusted to my personal preference. Your needs may vary.

Dead ends

Although this looks simple, I tried a couple of other things before settling on this solution.

  • Putting the list in a paragraph - I considered this as an option, but was unable to get Blogger's template language to do it. You could do it as a space-separated list, but as soon as you add separators of any sort -- like commas -- there's no apparent way to avoid putting a separator in at the end. Blogger supports an "isLast" property on labels when used in the post widget, but it does not do so in the labels widget. On further consideration I decided that this was probably a bad approach, anyway, since it's not as accessible as using real lists.

  • Changing font size based on post count - This is a relatively new twist in Web design. Flickr's tags are a popular example. However, I couldn't figure out a way to do it without resorting to Javascript. Blogger has a very minimal expression language for templates, but I haven't been able to find any detailed documentation for it. The closest I came was doing something like this:

    <font expr:size='data:label.count'> ... </font>

    But that didn't cap the maximum font size -- potentially leading to problems as I write more posts in each category. For now I've tabled this idea. It would still be interesting, but I think the best way to do it in Blogger would be to leave the base template HTML as-is, then dynamically modify the HTML with Javascript on the client side.

  • Adding some interesting CSS decoration - I considered doing this too. But the only decent decoration I could think of was a rounded button look similar to what Safari uses in its Bookmarks toolbar. That would have been possible, but I'd need to create and edit some images and use this method to get the rounded corners. Worse, it wouldn't display correctly in Internet Explorer so I'd need to do a CSS hack to hide it. So I tabled this idea too. Even so, I might look back into this in the future.

More template tips

Other entries in this series:

Thursday, November 02, 2006

Blogger: Image as Blog Header  

I'm going to do a brief series about how I wound up customizing my Blogger template (styles and widgets) after switching to the Blogger beta. First up, how I replaced the header with an image.

Why customize it?

Like a lot of people, I prefer to use an image instead of text for the header of my blog. I also want it to be clickable and always be a link back to the "home" page of the blog, which is a widely-adopted convention.

But I do not want to simply encode an inline image. For reasons of accessibility, I prefer a normal H1 tag that gets translated to an image by CSS.

Since I've been trying to keep the look-and-feel of my old template, I started with this image from my old template. It's 790 pixels wide by 105 pixels high.

Widget modifications

Here are the modifications I made to the default "Header" widget to accomplish this. Data that is specific to my blog, and which you'll want to change if you copy this code, is displayed in red.

<b:widget id='Header1' locked='true' title='Header' type='Header'>
  <b:includable id='main'>
    <div class='titlewrapper'>
      <h1 class='title'>
        <a href='http://drewthaler.blogspot.com/'><span/><data:title/></a>
      </h1>
    </div>
    <div class='descriptionwrapper'>
      <p class='description'><span><data:description/></span></p>
    </div>
  </b:includable>
</b:widget>

Widget explanations

The original Blogger template had a conditional so that the header was only a link if we weren't on the main page already. I removed the conditional because I think it makes more sense to always make it a link.

The original template also used 'data:blog.homepageUrl' for the link, but this resolved to 'http://drewthaler.blogspot.com/index.html', which is redundant. I wanted the shorter and more future-proof URL 'http://drewthaler.blogspot.com/' so I changed the link manually.

I left the description code as-is, because I don't use it. You may want to do something different.

You'll notice a single <span/> tag in the middle. That's required for CSS image replacement.

Stylesheet modifications

Here are the CSS additions that go with it. These are additions to the original stylesheet.

/* Replace the header with a graphic on computer displays */
@media screen, projection, tv {
  h1.title {
    width: 750px;
    height: 89px;
    left: 0;
    top: 0;
    padding: 15px 0 0 40px;
    margin: 0;
    border-bottom: 1px solid black;
    z-index: 0;
  }
  h1.title a span {
    background: url(http://homepage.mac.com/drewthaler/images/blog-header.gif) no-repeat;
    position: absolute;
    top: 0;
    left: 0;
    display: block;
    width: 790px;
    height: 105px;
    z-index: 1;
  }
  #sidebar {
    z-index: 2;
  }
}

Stylesheet explanations

The @media directive means that these styles should only be applied for certain types of media. For screen, projection, and tv renderers I prefer the nicer look. But they will not be applied for screen readers and printing.

I'm using the Gilder/Levin Method for CSS image replacement. In this method, the span does all the work: it gets resized and repositioned, and the image is displayed as a CSS background image for it. The image is clickable because it's on the span, and the span is inside the link tag.

The span is given a z-index of 1 so that it sits above the original H1. The H1 text is still there, hiding underneath at z-index 0. Then the sidebar is given a z-index of 2 so that it will float above the header image if the two overlap.

The pixel sizes (790px by 105px) are from the size of my image. The padding (15px 0 0 40px) and bottom border are in there to make CSS-on/images-off browsing still look acceptable. It's a personal preference and based on the size of my image and text, so you might need to tweak it for your setup. The H1's width of 750px is the image's width (790px) minus the horizontal padding (40px), and the H1's height of 89px is the image's height (105px) minus the vertical padding (15px) minus the one-pixel bottom border (1px).

Tuesday, October 31, 2006

When Benchmarks Attack  

In my professional life I've found myself measured by an external benchmark many times. One time that comes to mind was when I was writing disk drivers for Mac OS 8.

The disk driver's job in Mac OS 8 was simply to pass requests from the filesystem layer to the underlying storage medium. Requests would come in for such-and-such amount of data at such-and-such an offset from the start of the volume. After some very minimal translation, we'd pass this request onward to the ATA Manager, or USB Manager, or FireWire libraries.

(Sounds easy, right? Well, yes, but the devil was in the details. USB and FireWire supported "hot-plugging", i.e. attaching or removing the drive while the computer was still running. Supporting this in an OS that was never designed for it was a big chore. CD and DVD drives were also much more difficult to handle than regular hard drives, for similar reasons.)

But we ran into a problem as we were preparing our drivers for release. The hardware manufacturers that were buying our drivers wanted to make sure our drivers were "fast". So they ran disk benchmarks against our drivers. Not an unreasonable thing to do, you might say, although as I've stated there really wasn't a lot of code in the critical path. The problem is that most of the disk benchmarks turned out to be inappropriate as measures of driver performance.

Measuring weight with a ruler

One common disk benchmark of the time was to read sequential 512-byte blocks from a large file. In Mac OS 8 these reads were passed directly down to the disk driver.

Remember, the disk driver's job was supposed to be to simply pass the incoming requests on to the next layer in the OS. The vast majority of the time would be spent accessing the hardware. So no matter what the requests were, in effect this test should have returned almost exactly the same results for all drivers, with code differences accounting for less than 1% of the results. Right? Wrong.

As we ran the tests, we found out that on this particular benchmark, our drivers were much slower than existing third-party drivers (our competition, more or less). Dramatically slower, in fact -- if we ran the test in 100 seconds, they ran it in 10 seconds.

Upon further examination, we found out the reason why they did so well on this benchmark. They had already been optimized for it!

We discovered that the other drivers contained caching logic which would cluster these small 512-byte sequential reads into larger 32KiB or so chunks. Doing so would decrease the number of round-trips to the hardware needed, which increased their performance on this benchmark.

Do what sells, not what's right

Now, it's important to understand that this tiny-sequential-read benchmark doesn't even reflect real-world use. The fact that larger I/O requests perform better than smaller I/O requests has been well known for decades, and almost every commercial application out there used large I/O requests for this very reason. Buffered I/O is built into the standard C libraries, for pete's sake. In the real world, requests that came into the disk driver tended to be either large or non-sequential.

Modifying our drivers to do the same thing would basically be pointless -- a lot of work for very little performance difference.

(It only gets worse when you realize that our disk driver was directly underneath the Mac OS disk cache. A driver-level cache was almost completely redundant. And the right place to do this sort of readahead caching would have been there, in the existing OS cache layer, not in each individual disk driver.)

But ... sigh. The real world doesn't always make sense. These small sequential reads were a well-known benchmark, even if it was a disk benchmark and not a driver benchmark. And that simple fact elevated this atypical scenario into the eyes of a lot of people who didn't really understand what it meant. To them, if our driver didn't do the same thing as the competition, we wouldn't measure up.

De-optimizing to improve the benchmark

So in order to make sales we were forced to burn another month adding and testing driver-level caching so that we'd perform better on this benchmark.

The irony? The work we did to improve this atypical benchmark actually increased the amount of time we spent in the driver code, and sometimes increased the amount of data we read, which in turn decreased real-world performance by as much as 1-2%.

But hey, at least that benchmark got faster.

And we sold our driver.

Monday, October 30, 2006

Switched to Blogger Beta  

Yesterday I moved this blog to the new Blogger Beta.

I went whole-hog and switched to a new template as well. I'm still tweaking it, and XHTML compliance is temporarily gone. But for the most part things should be back to normal. RSS readers may see that every post has been modified. This was a side effect of the switch, although in fact I added categories/labels to all posts once I saw that they were going to be touched anyway.

Why switch? The mainline Blogger service (1.0) has been having boatloads of problems over the past month or two. It hasn't been very well maintained and it seems like it's just barely holding together. All the Google blogs are using the beta now, and in fact there are some very nice features in the beta that I was tempted by.

Cool stuff that you get from the switch:

  • MUCH faster publishing. 1.0 generated a zillion static HTML files, the 2.0 beta uses a database.
  • Labels, aka categories.
  • Backlinks, aka trackbacks. Better than MT's, since these are automatic. (Presumably filled in by Google crawls?)
  • Comment feeds, both global and per-post.
  • Better template management. It's more powerful and exposes more, and it's smart enough to let you add and rearrange certain things on your page with no effort.
  • Widgets, aka server-side directives in your template.

Downsides of the switch:

  • RSS reset. Everything in my RSS feeds got reset when I switched. Not a huge problem.
  • Porting customizations. To get some of the nicer features you need to upgrade your template. If you had your blog moderately customized, and change your template like I did, then it can be kind of a chore to port your customizations over. For me it took about a day.
  • Limited Safari support. Posting from Safari works just fine. Template editing seems like it has to be done in Firefox.
  • It's a beta. And knowing Google, it will probably remain a beta for another three years or so. Dude. Can't you guys commit?

I haven't seen any other major downsides yet, but I'll let you know.