Display: block and form legend elements

If you’ve ever tried to make an HTML form look like the fancy PSD mockup that your graphic designer threw together, you know how much hate forms deserve. Forms are an integral part of the web, yet making them semantic, accessible, and visually appealing across browsers can be a major bitch.

There’s lots of information scattered about the interwebs about arm-wrestling forms into submission, but I recently ran across an issue that has not, so far as I know, been adequately discussed. That topic is legends.

No, not like the legend of Jeffrey Zeldman and his big blue ox, although that’s a good one; I’m talking about the legend element that attaches to fieldsets in order to let your visitor know what the heck this group of form controls they’re supposed to fill out pertains to. Legends, it turns out, are some of the most annoying elements to style you can find. Not only do they by default have some bizarre formatting applied to them by every browser out there (usually in slightly different ways), but normal styling rules don’t apply. Display: block, anyone? Yeah, ain’t happening.

Recently I was asked to style a really nicely laid out form; it had everything lined up down the left side, clearly labeled sections, the works. Except that the headings for each section were occasionally long enough to wrap, and were displayed with many of the same rules as the H3’s on the page. Easy enough, I figured, I’ll just stick display: block and some margins on them.

Which is when I discovered that display: block has no effect on legends in any browser (any browser worth testing in, that is, which means IE 6, IE 7, Firefox 2, Firefox 3, and Safari for my company). Legends with lots of text in them tend to stretch themselves or, worse, their parent elements out to keep everything on the same line, and even if they’re supposedly block-level elements with a margin they’re still treated as inline. Not cool. Fortunately, my latent CSS superpowers were activated when I started swearing like a sailor (wish I had a more socially acceptable trigger, but there you go) and I pounded out some CSS that:

  1. Forces long legends to wrap in IE, Firefox, and Safari
  2. Encourages legends to act like they know what display: block is all about

The code looks like this (or see it in action):

HTML

<fieldset>
    <legend><span>Legend text</span></legend>
</fieldset>

CSS

legend {
    margin: 0 0 15px;
    float: left;
    white-space: normal;
    *margin-left: -7px;
}

legend span {
    width: 400px;
    display: block;
}

Seem weird? That’s because it is. Here’s the reasons for the insanity:

If you’re like me, you’ll want a margin on your block level elements; in order to get your margin recognized in Firefox 2, you have to put it on the legend element. Of course, margins won’t be recognized on inline elements or legends, but since display: block does squat for a legend you have to float the legend left. For the full display: block effect, of course, there should be a width on the legend, but since Firefox will collapse the legend’s width down to its content anyway, I left it off.

Instead, the semantically meaningless span gets the width and display: block. The span not only forces Firefox to bump the legend’s width out the correct amount, but it also is necessary for long legends to wrap in some browsers. I’m using a pixel width mainly because IE 6 requires a different pixel width (more on this later). Although in a perfect world your legends would not have anything in them other than the legend text, we don’t live in a perfect world.

Adding white-space: normal to either the span or the legend will fix the wrapping problems in Firefox 3 (I chose to attach it to the legend in the hopes that in fifty years or so I can rid myself of that span with less effort).

The *margin-left: -7px line is a hack to fix an IE-specific problem. Even with no padding, margin, and left: 0px IE will still indent your legends seven pixels. The star-property hack targets both IE 6 and 7 (while being ignored by sane browsers) and bumps the legend back over where it should be. I’m using the star-property hack here because it makes the source a lot less verbose; however, you should probably migrate that rule into an IE-specific stylesheet behind a conditional comment.

There is also one more IE-specific adjustment that you have to make thanks to the seven pixel indent. When calculating its box model, IE 6 will determine the width of the legend and add the seven pixel indent before shifting the legend to the left. Long and short of it is that the parent element will be bumped out seven pixels wider than it should be, so you’ll need to give the span a width seven pixels less than your target width for IE 6. For instance, to make the code above work in IE 6 I would add an IE 6 conditional comment with legend span { width: 393px; } inside somewhere.

And that’s it! You will, of course, want to be using an intelligent CSS reset (I recommend Eric Meyer’s CSS reset), but otherwise the above CSS should give you a legend element that acts like a block-level element instead of some weird border-loving pseudo-element with wrapping issues.

There are some further gotchas to be aware of if you start playing around with legends. IE by default colors all legends blue, for instance, and if you want the legend to overlay the fieldset border (or not) you’ll need to go about things slightly differently. However, if you’ve been frustrated by the inability to get your legends to behave like display: block means something, hopefully this snippet will help you out.

As is usually the case, I discovered this particular combination of HTML and CSS by taking previous solutions, mixing them into a putrescent green cocktail, and force-feeding it to my browsers, tweaking the constituent ingredients until they were saying “Mmm, tasty!” instead of puking a bunch of crap all over my lovely webpage. Specifically, John Faulds provided some basic concepts and examples for styling legends and Stephanie Sullivan wrote up the strategies for encouraging legend wrapping in Firefox 2 and 3. Thanks, guys; you rock.

If you want to see the code in action with various permutations for More Pretty, check out this page o’ examples.

Accept text from either LaunchBar or Quicksilver in Applescript

Here’s the thing: I absolutely adore LaunchBar. I use it constantly throughout the day, and if I’m using a computer without LaunchBar I practically can’t function because I keep habitually hitting the LaunchBar shortcut and opening Spotlight on accident. So when I write Applescripts that I want to accept text and do something with it, I use LaunchBar-specific code.

However, there’s a large number of people out there who prefer Quicksilver (link to Google Code project; also see Blacktree’s site). I don’t know if it’s for the sexier (if more complicated) interface or just because they’ve got Quicksilver embedded in their fingers as badly as I’ve got LaunchBar in mine, but they’re unlikely to switch anytime soon.

And then there’s the folks out there who haven’t discovered the joys of a launcher program and just run Applescripts manually or with something like FastScripts.

All of which adds up to a bit of a conundrum if you want to share your favorite Applescripts with the world. Fortunately, with a bit of Googling and some good old trial and error, I’ve written a simple Applescript template that allows all users, no matter how they like to launch scripts, to use yours. Without further ado, here’s the code:

-- You'll want to rename this function something appropriate
on action_function(someText)
    -- Check to see if we've got text, ask for it if not
    if someText is equal to "" then
        set question to display dialog ("Enter text:") default answer ""
        set someText to text returned of question
    end if
    -- Do whatever your script does here
end action_function

-- Quicksilver tie-in code
using terms from application "Quicksilver"
    on process text qsText
        my action_function(qsText)
    end process text
end using terms from

-- LaunchBar tie-in function
on handle_string(lbText)
    my action_function(lbText)
    open location "x-launchbar:hide"
end handle_string

-- Call the function in case the script was run directly
-- (Don't worry; this line won't execute if called from LB or QS)
my action_function("")

If you’re familiar with Applescript, the code should be pretty self-explanatory. You wrap all of the scripts actions into a function (called action_function in the example), and then call that function using the specific access routines for Quicksilver or LaunchBar (with a normal call to the function included just in case the user directly accesses the script). The code within action_function is merely a fall-back in case it receives a blank string (you’ll likely only need the fallback if they are calling the script directly). I leave any more specific error checking to you.

This template specifically accepts text; if your script needs to process a list of files, for example, you’ll need to change the code for the various programs appropriately (how specifically to do that I leave up to you, but the general template should still serve you well). One caveat is that if you’re compiling the code, you’ll need Quicksilver installed (even if you only use LaunchBar). However, as far as I can tell, if you’re just running the Applescript you don’t need to have either program installed (or you can have just one or the other). The main downside of needing both to compile is that if you rely on your users to adjust config variables in your script, then they will need to install Quicksilver (unless you include instructions to remove Quicksilver’s code if they aren’t using it).

Let me know if you run into any trouble using this code! I haven’t run into any errors with it in my testing, but that certainly doesn’t mean that they don’t exist.

TextSoap 6 and my XHTML Suite of custom cleaners

In case you hadn’t heard, TextSoap was updated to version 6.0 a few weeks ago. I’ve waited on posting about it because I wanted to share some of my custom cleaners (you can jump straight to the download if you’re so inclined), and now I’ve finally found the time.

For those who know about TextSoap, version 6.0’s main benefit (at least from my point of view) is a vastly redesigned custom cleaner editor. Cleaners can now run text through sub-routines, there’s a quick regex reference right in the window, and you can attach notes to cleaner actions to remind yourself what the heck that complicated regex pattern is supposed to be doing. There are other improvements, as well, but the custom cleaners interface is where it’s at for me. If you’re curious, check out the release notes for the full scoop.

For those not in the know, TextSoap is a fantastic piece of software that allows you to make changes to plain and rich text both using built-in cleaners or custom cleaners that you define yourself by combining regular expressions and any of the built-in cleaners with familiar Automator-style rules.

Yeah, I know, it doesn’t sound too impressive, does it? But that’s only because you’re used to wasting a lot of your time on mindless repetitive tasks involving text. TextSoap not only provides an easy way to save sets of common text-based find and replace actions, but it allows you access to them from pretty much anywhere on your computer by integrating with popular programs via plugins, offering a system-wide contextual menu, or hanging out in the Services menu.

When I first bought TextSoap, I regretted it because I barely ever used it (this was back in version 4.0, I think). Then one day I was doing something incredibly repetitive with text (I don’t even remember what), and I got fed up, launched TextSoap, and took a look at the custom cleaners. I’ve never looked back. Although the most powerful custom cleaners require knowledge of regular expressions, there are still hundreds of things you can do without ever worrying about regex simply by combining TextSoap’s provided cleaners with the building blocks available in custom cleaners. TextSoap provides an approach to text manipulation that has saved me hundreds of hours of drudgery.

Over time, I’ve found that the custom cleaners I create tend to fall into two categories:

  1. Cleaners that address specific problems that either recur or only happen once but require the same actions repeated a bunch in that sitting. For instance, for one client I have to convert a Word document into a newsletter every two weeks, observing their byzantine rules for HTML formatting. The first time I did it, it took a mind-numbing four hours. The second time, I created a custom cleaner while I worked and it took me two. The third time all I had to do was use the custom cleaner, and it took me one. With practice, I’m now down to about forty minutes.
  2. Cleaners that address generic recurring actions. These are cleaners that I’ve slowly tweaked over time, and now use primarily as building blocks for my task-specific cleaners.

It’s this last type of cleaner that I would like to share with you.

My XHTML suite of custom cleaners

My main use for TextSoap is manipulating HTML, and because I know a lot of other people out there have to do this on a regular basis I’ve decided to share the basic cleaners that serve as the foundation for my workflow. TextSoap has revolutionized how I perform certain tasks (particularly converting styled text to HTML and converting really hideous HTML into tasty XHTML), and I strongly recommend it to any web junkie who has cursed out a previous developer for their table-filled monstrosity of a website. Before I get into the nitty-gritty details of what’s included, here’s the download:

Download TextSoap XHTML Suite

Included are eight custom cleaners (if you’re only interested in one or two, see the ReadMe for details on which cleaners require one of their brethren):

  • Encode Ampersands. This encodes every ampersand that isn’t already part of an HTML entity.
  • Escape Single Quotes. Primarily useful for Javascript, PHP, etc., this escapes all single quotes with a backslash.
  • HTML Curly Quotes. For those clients who must have curly quotes, this is your solution. It converts every quote outside of HTML tags into a curly. (Please note: only works for English curly quotes.)
  • HTML Paragraphs. This converts text blocks separated by double line breaks into paragraphs, and converts single line breaks to <br /> tags.
  • Style to HTML. One of my workhorses, this cleaner takes richly formatted text and turns it into simple, paragraph-delineated HTML with appropriately placed strong and em tags.
  • URLs to HTML Links. This cleaner finds all of the easily recognizable URLs in a document (starting with http, https, or www) and converts them into HTML anchor links.
  • WebHappy. This happy little cleaner simply converts richly styled italics and bold into strong and em tags, straightens all quotes, and converts any problematic characters into HTML entities.
  • XHTML Cleaner. This is a pretty hefty cleaner, and I run it by default on any HTML that needs serious love to turn into XHTML. The cleaner performs a laundry list of common tasks (properly escaping self-closing tags, b to strong, lowercased tag and attribute names, etc.) and also attempts to add and remove linebreaks so that you can easily indent the code in your favorite editor (like Textmate). I rarely use XHTML Cleaner directly, but it offers a great starting point for any custom cleaner that needs to deal with poorly written HTML.

Although some of these cleaners are great on their own (I have a special place in my workflow for WebHappy, for instance, even if I never did think of a good descriptive name for it), a lot of them work best as the starting place for your own task-specific custom cleaners. I’ve tried to add notes to all of the regex rules, as well, so they may help you figure out how best to perform your own tasks (keep in mind that some of these cleaners were developed while I was still figuring regex out, so some of those regular expressions are narsty). If you improve or otherwise modify any of the core suite of cleaners, drop me a line because I’d love to see what you’ve done.

Enjoy!

Coda 1.6 released, minor TEA for Coda update

Coda 1.6 has been released, and boy howdy is it exciting! Coda now includes a plugin architecture, including Cocoa plugins for people who need to be able to manipulate the interface and an easy-to-use plugin creator that will allow you to run plugins using command-line languages (similar to Textmate). This is a fantastic update, and as we start to see plugins being produced I don’t doubt that Coda will become more and more appealing for Textmate users who have been holding out. It’s certainly not as powerful and flexible (no tab stops, for instance), but the addition of user-generated plugins will certainly allow people to do great things.

Along with the update to Coda, I’ve updated the TEA for Coda bundle (find the most recent version on the dedicated TEA for Coda page); all of the scripts will now perform their actions in the active document even if multiple windows are open. Additionally Indent New Line should be significantly improved performance-wise. The scripts do now require Coda 1.6, though; if you’re running an old version of Coda expect buggy behavior.

I am unlikely to improve the TEA for Coda scripts anymore; my new goal will be to get the Textmate bundle items that I know and love into Coda using the plugin interface. This will necessitate learning some Cocoa, though (since some of the key actions require user input), so it may be a little bit of time before I’m able to get a working plugin up and running. In the interim (or until someone else does it), TEA for Coda is still the best way that I know to get Coda to behave like a full-blooded HTML editor.

TEA for Coda now Universal Binary

I’ve posted a minor update to my TEA for Coda bundle. This update includes two changes:

  • The HTML Tidy script is now a universal binary! At long last, our PPC friends can run it just as easily as those with newer hardware.
  • The Format with Em and Format with Strong scripts now keep your text highlighted rather than moving the cursor to the end. This is not only more congruent with Textmate’s behavior, but I think it makes better sense, particularly if you ever need to wrap the text in more than one tag (or wish to add a class name, or…).

You can get the update from the dedicated TEA for Coda page. Unless you use PathFinder or have some other way of viewing invisible files, you’ll need to replace the entire HTML directory (if you’ve made changes to the scripts, you should copy them into the new directory before replacing the old one). If you are comfortable with invisible files, then you’ll just need to replace the .lib directory and the Format directory.

Monster

Samuel was working the drive-in at McDonald’s when Frankenstein’s monster drove up in a Hummer.

“Dude,” said Samuel. “Wicked neck bolts, man.”

“Raaaaurgh!” said Frankenstein’s monster.

“Hey, no prob,” said Samuel. “We all have days like that.” He put on his customer face, all raised eyebrows and sincerity. “May I take your order?”

“RAAAAurgh!” said Frankenstein’s monster.

“One number two with supersized fries, coming right up,” said Samuel. He punched some buttons on the greasy keyboard in front of him. “God, I wish my shift was up.”

“Raaaurgh! RAAAurgh!” said Frankenstein’s monster.

Samuel turned back to the window. “Hey man, I didn’t choose to be here. I just need some goddamn spending money. Never thought I’d end up hawking pseudo-food and doing my small part to perpetuate the evils of corporate America. But what can you do? Just gotta take what you’re dealt and make the best of it.” He eyed Frankenstein’s monster’s pallid demeanor and ragged stitching. “Course some of us get a worse deal than others, but judging by your wheels you’re doing alright.”

“RAAAAURGH!” said Frankenstein’s monster, pounding on the steering wheel.

“Dude, I am right there with you! All the hopeless consumption we engage in is just a distraction from what really matters, and here you and me are, buying straight into it. But you know, you have to eat and you have to get from place to place, so why not do it in style? Sometimes you just have to say to hell with it all, and live it up while you can. You’re destroying the environment and I’m working for the quintessential exploitative corporation, but at least in fifty years when we’ve consumed all our natural resources and our society is crumbling around us we’ll be able to look back and say, ‘Well, at least I had fun.'”

“Raaaaurgh!”

Samuel grimaced. “Yeah, sorry. I don’t get many opportunities to really talk to people on the job, though, you know? Most people don’t like to think critically about the implications of their lifestyle. They just drive up and want an automaton to hand them food.” Samuel stiffened his arms and moved them jerkily back and forth. “Would. You. Like. Fries. With. That. I mean, I guess we’re all what society has made us, but still.” He paused and glanced at Frankenstein’s monster’s neckbolts and shrugged. “You more than most.”

A coworker walked up behind Samuel. “Hey, order’s up.”

Samuel grabbed the paper bag, stuffed some ketchup packets inside, and handed it out the window to Frankenstein’s monster. “That’ll be $7.59.” He grabbed the proffered bill, pressed a few more greasy buttons, and handed the change back. “And hey, thanks for letting me unload on you, you know? It’s nice to connect with people sometimes, and damned hard to do it working here.”

“RAAAAAAURGH!” said Frankenstein’s monster, and he peeled out of the drive-through, leaving Samuel coughing in his exhaust.

“What a jackass,” growled the wolfman in the backseat of the Hummer. “He totally ignored you when you ordered me a number seven. And you asked for it like five times.”

“Raaaurgh,” said Frankenstein’s monster sadly.

“Yeah, I know it,” said the wolfman. “Kids these days are so oblivious.”

Understanding Progressive Enhancement Better

This week’s A List Apart included Understanding Progressive Enhancement by Aaron Gustafson. I was pretty jazzed to read the article, because I’ve been hoping for a simple explanation of progressive enhancement to show my supervisor for some time.

Unfortunately, after reading Understanding Progressive Enhancement I realized that the article didn’t actually define progressive enhancement. It had a great description of the concept of graceful degradation, but when it got to progressive enhancement it became abstract to the point that I don’t think someone who isn’t already passingly familiar with the idea would understand it.

I am not a mover and thinker when it comes to web design and development, but at the least I can offer a complementary explanation for Gustafson’s workflow-oriented description.

As Gustafson says, graceful degradation focuses on building out a great site, and then testing on older browsers and tweaking things to make sure the users have at least a half-assed experience.

Progressive enhancement has very similar results (cutting-edge users get the best experience, users with older or otherwise limited technology get a less ideal experience), but comes from a different angle. Gustafson says that progressive enhancement is all about the content, and then goes into a long, slightly flawed metaphor about Peanut M&Ms. Mildly wonky metaphor aside, he’s right about content being the important thing; he just needs a succinct definition and concrete examples.

Progressive enhancement is the idea that you should design your content in a way that is accessible no matter how your users access your site, and then add Javascript interactions and advanced CSS afterwards to make the lives of people who have capable browsers easier and more beautiful.

For me, I find this best illustrated with a pair of examples.

I have two sites I have been working on recently. For both of them I am the interface coder; I receive Photoshop designs, and am responsible for building them in HTML, CSS, and enough PHP to tie into the developer’s backend. The first project is a Web 2.0 startup that I inherited from a previous developer. The second was designed by a professional print and web design house and handed off to me for conversion to HTML.

For the first site, the code I inherited uses a lot of Javascript to make the interactions work. The user enters the site, types some information, and things swoop across the screen to quickly lead them through what the site has to offer before depositing them on a signup form. If the user has Javascript disabled links don’t work, the form fields remain populated with filler text (apparently a background image), and the user never sees the signup form. This site was designed for (eventual) graceful degradation. The designers and coder took their perfect world vision and implemented it, and were planning to go back over everything once it was done to make sure that some semblance of a workflow would exist in older browsers or browsers that don’t support Javascript.

For the second site, the designers provided me not only with the Photoshop files, but with a complete user experience description. The user experience description included a semi-interactive Flash file on the homepage, an automatically scrolling list of article titles that reacted to mouse movements, and other bells and whistles. First, I built out the in HTML and CSS and once I had tested everything I added the Flash and Javascript interactions (making sure that should those technologies not be present the site would fallback to my original vanilla designs).

This second approach is a limited example of coding using the idea of progressive enhancement. I knew that what was important was that no matter what technology the user had available, they should be able to reach the content, so in my first iteration of code all links lead somewhere, all navigation elements were present, and even a user with a stripped-down mobile browser would be able to navigate and read the site (even if it looked terrible). The Javascript and Flash thus enhance the user’s experience, but are not necessary for the user to experience the basic content and interactions the site has to offer.

The problems with graceful degradation in the first site should be self-evident. By focusing solely on making their interactions slick and perfect immediately, the first coder crippled the site for any number of users. Content delivery in all situations isn’t the focus; instead “fixing” old browsers will eventually be the focus.

The reason progressive enhancement is an important idea for web designers to get their heads around is that if you use progressive enhancement to guide your design you don’t have to patch obvious failings but instead build an ever-improving user experience on a solid foundation. Not all browsers may look and feel the same, but regardless of the tools at their disposal users will be able to access and interact with your site’s content in ways that were part of your overall design. Even if they can’t articulate it, users can tell when their experience is an afterthought rather than a priority, and as a result you only stand to gain.