June 30, 2011 4

Ubiquity in Firefox 5

By in ubiquity

As may be clear by my ubiquitycommands github repository, I am an avid Ubiquity user. So much so, in fact, that I delayed upgrading to Firefox 5 until I was able to find a version of Ubiquity that works with Firefox 5. Wonder of wonders, I have found that version.

Satyr Murky continues to work on a community-maintained fork of the original Mozilla.org version of Ubiquity. Luckily for us, it works in Firefox 5.

Thank you, Satyr!

Download: Ubiquity (tip.xpi)

 
 

Tags: ,

June 27, 2011 0

Test Your Transformations

By in .net

Web.config transforms are a really great tool for setting up environment-specific configuration files. However, the transform syntax can be a bit obscure, and tracking down configuration bugs is just painful.

For this, there is a great testing tool hosted on AppHarbor. Paste in your web.config contents in one box, drop in your transform in another and out comes the transformed web.config. Never waste time messing with your web.config transforms again!

screenshot of the web.config transform tester service

 
 

Tags: , , ,

June 20, 2011 1

Google Calendar > Lotus Notes

By in productivity

Are you forced to use Lotus Notes at work? Do you prefer Google Calendar? Would you like to have your Lotus Notes calendar synced to your Google calendar? Solution: CalSync!

CalSync is a great little utility that runs in the background and automatically syncs your Lotus Notes calendar with Google Calendar. You can download it from SourceForge. It’s a standalone application that doesn’t require installation. Simply extract the zip file into a directory of your choosing and open the executable. You’ll need to provide you Lotus Notes account information, as well as your Google account information. (I recommend using Google’s 2-factor authentication and setting up an application-specific password for CalSync.)

screenshot of calsync account info dialog

After providing your account information, you can configure how you would like it to sync with Google Calendar. Personally, I recommend creating a new Google Calendar that is dedicated to syncing with your Lotus Notes calendar.

screenshot of calsync account options dialog

There are plenty of additional configuration options to customize how/when your calendar items are synced. And, of course, to have it start with windows, simply add a shortcut to CalSync to your Startup folder. Now you can get your Lotus Notes work calendar on your smartphone via Google!

 
 

Tags: , ,

June 16, 2011 0

Userscripts, Userstyles and Ubiquity

By in ubiquity, userscripts, userstyles

A while ago I described how to merge two git repositories into one. I discovered how to do this as I was attempting to consolidate a number of git repositories that I have on GitHub. Previously, I had a separate git repository for each user script, user style and ubiquity command that I’ve written. It was getting unmanageable so I decided to create a single git repository for my user scripts, another for my user styles, and a third for my ubiquity commands. You can now find my them all on GitHub.

Userscripts

[GitHub repository]

Tweetapaper
adds ‘Tweet’ links to articles in your Instapaper queue. [install from Userscripts.org]
Instasubmit
auto-submits the Instapaper Add form when loaded via Google Reader’s Send To Instapaper action. [install from Userscripts.org]
Radapaper
adds ‘Radbox’ links to articles in your Instapaper queue. [install from Userscripts.org]
TreasuryDirect Login
makes the password field editable so that you can use the keyboard to log in. [install from Userscripts.org]

Userstyles

[GitHub repository]

Gmail – Icons for S/Mime Messages
adds icons to messages in Gmail which have been digitally signed or encrypted using S/MIME. [install from Userscripts.org]
Tab Notifier
highlights any open Gmail, Google Reader, Google Voice, and Facebook tab when there are unread items. [install from Userscripts.org]

Ubiquity Commands

[GitHub repository]

ClaimID
easily claim URLs under your ClaimID account. [install from GitHub]
Wishlistr
easily add items to a Wishlistr wishlist. [install from GitHub]
 
 

Tags: , ,

March 23, 2011 7

Merge Two Git Repositories Into One

By in git

A few weeks ago I tweeted: “Just did 2 subtree merges in order to combine 2 partially-related git repos into a single repo and still maintain history. #gitrocks” Wanna learn how to do it? Here we go…

TL;DR

# create new project as the parent
$ mkdir new_parent_project
$ cd new_parent_project
$ git init
$ touch .gitignore
$ git ci -am "initial commit"

# merge project A into subdirectory A
$ git remote add -f projA /path/to/projA
$ git merge -s ours --no-commit projA/master
$ git read-tree --prefix=subdirA/ -u projA/master
$ git ci -m "merging projA into subdirA"

# merge project B into subdirectory B
$ git remote add -f projB /path/to/projB
$ git merge -s ours --no-commit projB/master
$ git read-tree --prefix=subdirB/ -u projB/master
$ git ci -m "merging projB into subdirB"

The most common use case for sub-tree merges, that I’m aware of, is to merge another git repository into a subdirectory in an existing repository. There are quite a few tutorials which cover these steps. In fact, the second two sets of commands above do exactly that. However, I discovered that in order to merge two repositories into a new repository, the new repository must already have a prior commit in it. Otherwise, the sub-tree merges will not work as planned. So, as in the first set of commands above, be sure to create at least one initial commit prior to doing the sub-tree merges. It can be as trivial as committing an empty file. But without it, the merges will not work correctly. Armed with this knowledge, you can follow the tutorial on GitHub on merging sub-trees. Or you can follow along with me here.

Create Parent Repo

First, create a new, empty project to act as the parent project for our two existing repositories.

Jason@BRUTUS ~/dev
$ mkdir parent

Jason@BRUTUS ~/dev
$ cd parent/

Jason@BRUTUS ~/dev/parent
$ git init
Initialized empty Git repository in ~/dev/parent/.git/

Now we need to create the initial commit. This is essential.

Jason@BRUTUS ~/dev/parent (master #)
$ touch .gitignore

Jason@BRUTUS ~/dev/parent (master #)
$ git ci -am "initial commit"
[master (root-commit) fc6f5ad] initial commit
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 .gitignore

Merge Project A Into Subdirectory

Next, we add a remote to the first project we’d like to import. We’ll give the remote a name (projectA) and pass the -f option so that it will fetch the contents of this remote immediately.

Jason@BRUTUS ~/dev/parent (master)
$ git remote add -f projectA /path/to/projectA
Updating projectA
warning: no common commits
remote: Counting objects: 16, done.
remote: Compressing objects: 100% (16/16), done.
remote: Total 16 (delta 7), reused 0 (delta 0)
Unpacking objects: 100% (16/16), done.
From /path/to/projectA
 * [new branch]      master     -> projectA/master

Now, let’s run a merge but not commit the result (--no-commit flag). We also need to specify the merge strategy ours with the -s switch.

Jason@BRUTUS ~/dev/parent (master)
$ git merge -s ours --no-commit projectA/master
Automatic merge went well; stopped before committing as requested

Now that we are in merging mode, we’ll read in the tree from the remote, taking care to provide a subdirectory into which the subproject will go. This is specified with with --prefix switch. Also, add the -u flag to update the working tree with our changes.

Jason@BRUTUS ~/dev/parent (master|MERGING)
$ git read-tree --prefix=projA/ -u projectA/master

The remote has been merged into its own subdirectory and the changes have been staged. Now we can simply commit them.

Jason@BRUTUS ~/dev/parent (master +|MERGING)
$ git ci -m "merging project A into subdirectory"
[master 4d2d50d] merging project A into subdirectory

Merge Project B Into Subdirectory

At this point, we have Project A merged into its own subdirectory within our new parent project. Merging in Project B uses the same simple steps as above.

Jason@BRUTUS ~/dev/parent (master)
$ git remote add -f projectB /path/to/projectB
Updating projectB
warning: no common commits
remote: Counting objects: 47, done.
remote: Compressing objects: 100% (47/47), done.
remote: Total 47 (delta 23), reused 0 (delta 0)
Unpacking objects: 100% (47/47), done.
From /path/to/projectB
 * [new branch]      master     -> projectB/master

Jason@BRUTUS ~/dev/parent (master)
$ git merge -s ours --no-commit projectB/master
Automatic merge went well; stopped before committing as requested

Jason@BRUTUS ~/dev/parent (master|MERGING)
$ git read-tree --prefix=projB/ -u projectB/master

Jason@BRUTUS ~/dev/parent (master +|MERGING)
$ git ci -m "merging project B into subdirectory"
[master 8f41792] merging project B into subdirectory

Pulling In Updates

If the original repositories (Projects A and B in this example) continue to live on elsewhere as separate projects, you can easily pull in updates to your new parent repo. Using the sub-tree merge strategy, the updates will be applied properly to the applicable subdirectory.

Jason@BRUTUS ~/dev/parent (master)
$ git pull -s subtree projectA master

However, if you no longer have any need for the original repositories, they can be deleted and the remotes in your new parent project can safely be removed.

Below is a screenshot of a repository after two sub-tree merges. The repositories that I merged were two separate userstyles: one for using tab color for notifications (gtab) and another for adding S/MIME icons to gmail’s inbox (gmail). Each of these two projects have their own history that was maintained after the merge. Now they are each in their own subdirectory in a common userstyles git repository. As you can see, the two projects each have their own lines of development that do not include any common ancestry until the merge point.

 
 

Tags:

February 22, 2011 0

Keeping Tabs On Your Credit Report

By in finance

Regardless of which financial blogger or author you follow, it is generally agreed that keeping tabs on your credit report is sound advice. By federal law, you are entitled to a free credit report from each of the 3 primary credit reporting agencies every 12 months. (See annualcreditreport.com and avoid all the credit report scams) Of course, that means in the worst case scenario you could potentially have incorrect or adverse information on your credit report for up to a year before it’s detected. A strategy that I employ to lessen this risk is to stagger each of my credit reports every 4 months. An example schedule could be ordering your TransUnion credit report every January, Experian every May, and Equifax every September.

Of course, there’s no guarantee that each of these reporting agencies will have the same information. Which means it’s possible that adverse information only exists on a single report. In which case, you’re back to the original situation of detecting this information up to 12 months after it’s added to your report. However, the odds are in our favor that adverse information is added to more than one of the different reports. This means, in the best case, you can detect adverse information at most 4 months after it’s added to your report.

In order to keep my schedule straight, I have scheduled a Google Calendar reminder for each of the three credit reports. Each reminder repeats annually and sends me an email notice so that I know from which agency the report should be ordered. It’s also a good idea to order a report for your spouse on the same schedule. So far, I like this method for keeping up on my credit health and lessening the risk that incorrect or adverse information negatively impacts my credit.

 
 

Tags:

February 15, 2011 0

CodeMash Recap

By in conferences

I can’t honestly call this a recap or an overview because there is just too much good stuff to do CodeMash proper justice. And I clearly waited too long to do a ‘best of’ or ‘highlights’ post. However, after attempting to give a recap of CodeMash to my fellow CSGers, I had a list of talking points and recommended resources that shouldn’t be put to waste. I’m omitting the two mini-demos that I gave during my recap: git-bisect and BDD/Cucumber. So you’ll have to research those on your own. Seriously, go try git-bisect. It’s cool.

So it was clearly impossible to recap 72 hours of awesome in 1 hour. Not to mention the drinking, water slides, etc. So here are a few resources you ought to check out while you’re sitting on the couch with your wife suffering through American Idol this season.

Reasons why CodeMash is awesome

  1. The tiki hut/bars.
  2. Waterslides. In Sandusky. In January.
  3. Hot-tub snowball fights; lacking only a time machine.
  4. Aside from the numerous presentations, there are great open spaces that spring up. These lead to great discussions and they’re completely ad-hoc. I participated in one covering node.js (check out the #nodemash tag on Twitter)
  5. You’ll see people pairing all over the place in corners, empty rooms, hallway couches. They’re working on mini-projects, trying out demos, and generally experimenting; usually inspired by a talk, keynote, or open space.
  6. You can attend a deep dive on Parallel Programming in .NET and then see an intro to UX design, followed by BDD with Cucumber, topped off with HTML5 and offline webdev. One of the best things about CodeMash is the range of languages, platforms, and technologies. You can meet people doing awesome stuff in Java or Flex, and pick up a few testing tricks from the Ruby dudes. Even within just the .NET world, there was VB, C#, F#, and IronRuby.
  7. Did I mention the after-parties?

Things to check out

If you’re interested in going next year, be prepared. They sold out in just 3 days this year. That’s right: the conference itself lasted longer than the ticket sales.

 
 

Tags:

January 28, 2011 6

Networked PDF printer? Yes, please.

By in home network

Need an awesome solution for printing to PDF? Have multiple home machines and prefer a network solution? Read on!

My setup involves PDFCreator installed in server mode on my Windows Home Server box. The printer is shared so other machines on the network can print to it. PDFCreator saves the PDF on the server under the user’s Documents folder. Using Windows 7 Libraries, a user’s Documents folder on the server is added to their local Documents Library so they have quick access to their printed PDFs.

Step by step process:

  1. Download and initiate the PDFCreator installer from Sourceforge on your home server box. Be sure to select Server Installation during setup and choose the remaining options as you please.
  2. Once installed, start PDFCreator and choose Printer -> Options from the menu. Open the Auto-Save settings and enable Auto-Save mode. Choose your auto-save file name and location (file name/location tokens available in the select box).
    I chose the save location: D:\shares\Users\<REDMON_USER>\PDFs\
  3. Now you can minimize PDFCreator (minimizes to the system tray) and go to your Printers control panel to share the PDFCreator printer. Right-click on the PDFCreator printer, choose Sharing and check Share this printer.
  4. On each of your networked machines, you can now go through the Add Printer wizard and add the PDFCreator printer by using the path: \\<server>\PDFCreator
  5. As an optional step (for Windows 7 users), you can add the user’s Documents folder (on the server) to their local Documents library. On any networked Windows 7 machine, open the user’s Documents library. Under the heading Documents library heading, click the link Includes X locations. Click Add and browse to the user’s Documents folder on your server.

At this point, your user’s can easily print to PDF. The generated PDF is saved on the server in their own user folder and is accessible through their Windows 7 Documents library. However, there is a catch. The PDFCreator monitor must be running to auto-save print jobs to PDF. If it is not running, the jobs will simply queue up and execute the next time someone logs into the server. (The PDFCreator monitor is added as a shortcut to your Startup folder.) To get around this issue, we simply need to turn PDFCreator into a Windows service.

  1. Download and install the Windows Server 2003 Resource Kit Tools.
  2. Download and install Any Service Installer.
  3. Fire up Any Service Installer and switch to Advanced from the mode menu.
    • Fill out the location of your Windows 2003 Resource Kit installation
      (C:\Program Files\Windows Resource Kits\Tools)
    • Select PDFCreator as the application you want to make a service
      (C:\Program Files\PDFCreator\PDFCreator.exe)
    • Enter your WHS Administrator username and password

Now you’re done and you can remove PDFCreator from your startup folder! Users can print freely and the print jobs will execute immediately.

I used two different step by step guides in the process of getting my PDF printer set up: the first for simply installing PDFCreator on a server and the second for turning PDFCreator into a Windows service. If anyone has any other PDF printing, Windows Home Server or home networking tips, be sure to share in the comments. I’d love to hear about any WHS tricks you might have come across.

 
 

Tags: ,

June 4, 2010 0

IE6, MooTools, and MultiBox: A Tale of Woe

By in javascript, mootools

On a recent project, I traveled to the depths of hell and came out alive. Here’s how it went down…

The initial task wasn’t all that unique or difficult. We were to add a few videos (most hosted on YouTube, with a couple self-hosted) that would open in a lightbox-style popup when the thumbnails were clicked. MooTools was already being used on the project, so we selected MultiBox by phatfusion as the pop-up of choice. I won’t go into the reason(s) for selecting MultiBox. The more I work with it, the less I like it; but that’s for another post. So, we have MooTools as the JavaScript framework and MultiBox as the plug-in.

Base Functionality

Keeping progressive enhancement techniques in mind, we set the destination URL of the thumbnails to be the video page on YouTube. This gives crawlers the ability to find the true video destination, as well as user’s without JavaScript the ability to find the video. Now, I realize that YouTube doesn’t even work without JavaScript, but that’s their problem. (On a side-note, it’s really sad that they use JavaScript to inject the Flash video player, when the SWFObject static publishing method would work just fine).

So the first problem we run into is that, by default, the MultiBox media type of youtube expects the link URL to point directly to the video and not to the video page on YouTube (one reason to ditch MultiBox). Note the difference in URLs below. The video identifier is the same in both (eCzEkiIucUk) but rather than passing it as a parameter to the ‘watch’ page, we can pass it as the slug to /v.

Video page on YouTube: http://www.youtube.com/watch?v=eCzEkiIucUk
Actual video on YouTube: http://www.youtube.com/v/eCzEkiIucUk

To deal with this, I wrote a quick script that executes ondomready. It parses the v parameter value out of the query-string and rewrites the URL to point directly to the video. This way, when MultiBox opens and inspects the URL to load, the video itself is opened in the MultiBox pop-up rather than the entire YouTube page.

Roadblock #1 : MultiBox ‘types’

At this point, everything is working as expected until the client requests that we provide a customized fallback message for users without Flash. This is a perfectly reasonable request. Ideally the fallback content would be a transcript of the video and perhaps some still images from the video. Maybe even a link to the raw H.264 video. Of course, in this case we are requested to simply provide a ‘Please install Flash’ message. Unfortunately, MultiBox doesn’t have a way to provide custom fallback content for its youtube type. (Yet another reason to ditch MultiBox.)

Rather than using the youtube type and its out-of-the-box functionality, we’ll switch over to the element type. This type allows you to target an element in the dom and the targeted element (and children) is cloned into the MultiBox pop-up. For this to work, we’ll provide our own object element, complete with cross-browser nesting of objects (see SWFObject static publishing) and our own custom no-flash fallback content. Each video thumbnail will now have the actual video markup next to it in the source, hidden via CSS. The thumbnail is marked up to link to the video page on YouTube (for JS-off users). On page load, the links are rewritten to point to the fragment identifier of the video object in the page (e.g. href="#video1"). This way, when MultiBox is activated using the ‘element’ type, it will grab the object element with id="video1" and clone its contents into the pop-up.

Roadblock #2: MooTools ‘clone()’

With the exception of IE6, everything works as expected across browsers both with and without JavaScript. However, it fails in IE6 (of course). Upon inspection of the cloned object elements after they are inserted into the pop-ups, we notice that they no longer contain the param children they’re supposed to. The original object elements still contain their param children, but the clones do not. Internally, MultiBox uses the MooTools clone() method on the base Element class so I put together a quick test page to verify that MooTools’ clone() method works properly in IE6 on object elements. It failed the test, and I filed a bug against MooTools.

The workaround for the MooTools clone() bug is to use the old-school innerHTML property ourselves rather than allowing MooTools to do proper DOM manipulation in the background. So I open up MultiBox and find the routine whereby it uses clone() and add a quick hack. I check if the browser is IE6 and if so, use innerHTML to copy the object element (and all its children) into the pop-up.

Roadblock #3: IE6 ‘flashvars’

So now we have the object being correctly cloned into the pop-up when a user clicks the video thumbnail. An then I get another ticket. All the YouTube videos are playing, but the self-hosted video is not. Why is the video not playing? This particular video is played using a SWF player which takes the FLV file name to play via the flashvars param. I notice that the FLV file isn’t being requested in IE6. But we just fixed the param bug and the children are all there! Or are they really? After some more debugging and googling, I find that IE6 resets the value of the flashvars param when its innerHTML is modified.

You’re not surprised, are you?

After some more searching, I find a great blog post that describes the solution. Rather than passing the different variables to the flash player using the flashvars param, we’ll just add them to the SWF query-string. So this:

<object … >
  <param name="movie" value="player.swf" />
  <param name="flashvars" value="path=video1.flv" />
  …
</object>

becomes this:

<object … >
  <param name="movie" value="player.swf?path=video1.flv" />
  …
</object>

Since we’re using SWFObject’s static publishing method, there are 2 object elements. One intended for IE, and another one, intended for everyone else. The proper object (for Firefox, Chrome, et. al.) is nested within the IE object and hidden via Conditional Comments. Since this whole flashvars mess is only a problem for IE6, we only need to modify the outer object element.

Roadblock #4: MultiBox’s flvplayer

Just when you thought we were out of the woods, we have one last issue. Now that we have the video objects with proper fallbacks, and the elements being cloned properly, and the config file information passed correctly, we still don’t have a functioning video in IE. Since we modified the outer IE object element, thanks to the previous issue, we’ve broken the video in all versions of IE. What’s the problem this time? It turns out that the flvplayer.swf that comes packaged with the phatfusion MultiBox doesn’t stream the video properly when the FLV file name is passed via query-string. Solution? Ditch phatfusion’s flvplayer and use another! (I recommend any of the JW players if your situation fits the non-commercial bill)

When the bugs align…

So at the end of this ordeal, I had three more reasons to avoid phatfusion’s MultiBox widget, I had filed yet another bug against MooTools, and I had conquered yet another bug in IE6. And it all piled up because of a tiny bug in a particular flvplayer; that was caused by the solution to a specific bug in IE6; which was only a problem because of a bug in a particular version of MooTools; which was only a problem because of a poorly designed API in MultiBox; which was encountered because the client wanted to display a download button for Flash; mostly for users who aren’t even able to install Flash if they wanted to. Don’t you just love web development?

 
 

Tags: , , , ,

May 23, 2010 6

Cross Browser Inline-Block

By in css

How many times have you used float:left to make a bunch of elements align horizontally? Usually this technique is used on lists (of both the ul and ol persuasion) and generally works just fine under a few preconditions. The first precondition is that you are comfortable using some form of a clear-fix hack. Either you apply overflow:hidden to the container, or you add a meaningless empty div with clear:both, or you apply some magic with the :after pseudo-class. While this isn’t a deal breaker, it is annoying.

The second precondition is that all floated elements are of the same (fixed) height. Of course, your original mock-ups with dummy data and lorem ipsum text all use the same element just repeated across the page, right? And then the client provides actual content (hopefully), and you find that some items have a really long title, or a super-tall product image. And what happens? The floats ‘snag’ items and you end up with this:

float see fiddle

If the previous screenshot is what you going for, or both previous preconditions hold in your case, you may stop reading right now. Otherwise, read on and you’ll learn how to create this instead:

final see fiddle

First, I must give credit where credit is due. The technique I’m about to describe (and improve upon) was covered by Ryan Doherty over at the Mozilla WebDev blog. I suggest you read his post first because I’m going to breeze through his foundation and jump into my improvements.

Foundation

Here is the HTML will be working with:

<ul>
    <li>
        <h3>Product 1</h3>
        <p>Some product description.</p>
    </li>
    <li>
        <h3>Product 2</h3>
        <p>Some product description.</p>
    </li>
    <li>
        <h3>Product 3</h3>
        <p>Some really, super, long product description.</p>
    </li>
    ...
</ul>

And some aesthetic styles not relevant to the inline-block layout:

ul {
    list-style:none;
    width:440px;
}
li {
    border:1px solid #888;
    -moz-border-radius:3px;
    width:75px;
    text-align:center;
    margin:5px;
}
h3 {
    font-size:14px;
    font-weight:bold;
    margin:.25em;
}
p {
    margin:.25em;
}

Compliant Browsers

The first step is to try an use the proper CSS property: display:inline-block. This property gives an inline element, block-like properties. Such as the ability to have width and margin. With just this property, we have support in Firefox 3+, Safari 3+, Chrome 1+, Opera 9+, and IE8+. Not too shabby.

li {
    display:inline-block;
}

valign

Fixing the Rest: Firefox 2

Aside from IE 6 and 7, only Firefox 2 fails to support inline-block. To get support in Firefox 2, simply add the mozilla-specific property display:-moz-inline-stack prior to the display:inline-block declaration. Non-Gecko browsers will ignore the -moz rule. Firefox 3+ supports inline-block so it will override the -moz-inline-stack rule. Using the -moz-inline-stack rule for Firefox 2 also necessitates a div be wrapped around the inline-block element’s contents. Given Firefox 2’s latest market share numbers (most give it under 1% overall), I would generally concede that messing with -moz-inline-stack and an inner-wrapping div is unnecessary. For this reason, I have removed the -moz-inline-stack rule from my Inline-Block gist on GitHub, however, you can see it in action at jsfiddle. Feel free to add it back in yourself if Firefox 2 support is necessary.

Fixing the Rest: IE 6/7

IE 6 and 7 both support inline-block natively but with a caveat; they only support it on elements that are inherently inline. Thus, block elements like div and list-item elements like li won’t apply inline-block. However, IE 6/7 has the concept of ‘layout’. (see On Having Layout). IE treats elements with layout triggered exactly the way inline-block elements are supposed to work; that is, block-level elements that are displayed inline. So for IE6/7 we reset the display to inline, and trigger hasLayout with zoom:1.

* Note: You can apply the IE 6/7 rules in any manner you wish. Conditional Comments paired with IE-only stylesheets is generally the preferred method. However, I have gone with the *hack in this case to make these utility classes copy/paste-able.

li {
    display:inline-block;
    *zoom:1;
    *display:inline;
}

The final touch is to set vertical-align:top to make the boxes line up across the top:

li {
    display:inline-block;
    *zoom:1;
    *display:inline;
    vertical-align:top;
}

final

Room for Improvement

So now we have our elements aligned horizontally without using floats. We have one last problem which is where I will improve on Ryan’s method. In the screenshot below I have changed the li margin to margin:5px 0; We would expect the left and right borders of each box to touch, no?

whitespace

The problem is due to the fact that white-space surrounding inline elements is displayed. Of course, this makes perfect sense. Imagine if the white-space between words in a sentence weren’t displayed! The trick is to take advantage of letter-spacing and word-spacing to counter the white-space between our inline-block elements. (This is unnecessary in IE6/7 which already ignores the white-space between boxes because the elements have hasLayout triggered and are not technically inline-block.)

Six of One…

My first attempt was to apply a negative word-spacing to the container. Word-spacing is inherited, so we must reset it to normal on our inline-block elements themselves so as to not affect their children. With word-spacing set to -1em, we have eliminated the offending white-space in Firefox and Opera.

nowhitespace see fiddle

…Half a Dozen of the Other

In order to fix WebKit (Safari, Chrome) we must also apply negative letter-spacing. Interestingly, applying only letter-spacing actually fixes both WebKit and Firefox which means that either letter-spacing or word-spacing will work for Firefox. However, in order to appease Opera, we will apply both. see fiddle

All Together Now

I have pared down the necessary rules to a set of utility classes named ib-container and ib-block, as seen below. You can also find them in a gist on GitHub and as a fiddle on jsFiddle.

.ib-block {
    vertical-align:top;
    display:inline-block;
    *zoom:1; /* IE6/7 */
    *display:inline; /* IE6/7 */
}
.ib-container {
    letter-spacing:-.25em;
    word-spacing:-1em;
}
.ib-container .ib-block {
    letter-spacing:normal;
    word-spacing:normal;
}

Other Variations

They can be used on list items as seen in this example or any other set of sibling elements:

<div class="ib-container">
    <div class="ib-block">…</div>
    <div class="ib-block">…</div>
    <div class="ib-block">…</div>
</div>

They can be used without the ib-container class if the white-space between ib-block elements is not an issue for you:

<ul>
    <li class="ib-block">…</li>
    <li class="ib-block">…</li>
    <li class="ib-block">…</li>
</ul>

They can be nested so an ib-block element becomes an ib-container for other ib-block elements:

<ul class="ib-container">
    <li class="ib-block ib-container">
        <div class="ib-block">…</div>
        <div class="ib-block">…</div>
    </li>
    <li class="ib-block ib-container">
        <div class="ib-block">…</div>
        <div class="ib-block">…</div>
    </li>
    <li class="ib-block">…</li>
</ul>
 
 

Tags: , ,