Fall Sale! Code FALL2024 takes 25% OFF our Pro Plugins & Books »
Web Dev + WordPress + Security

Perfect Pre Tags

If you operate a website that features lots of code examples, you know how important it is to spend some quality time styling the <pre> element. When left unstyled, wild <pre> tags will mangle your preformatted content and destroy your site’s layout. Different browsers treat the <pre> tag quite differently, varying greatly in their default handling of font-sizing, scrollbar-rendering, and word-wrapping. Indeed, getting your preformatted code to look consistent, usable, and stylish across browsers is no easy task, but it certainly can be done. In this article, I’ll show you everything you need to create perfect <pre> tags.

First thangs first

Before getting into it, let’s take a moment to ensure we’re all on the same page. The (X)HTML <pre> element is used to display preformatted text, code, or just about anything else. pre tags are ideal for multiple lines of code or text that need to retain character spacing, display unformatted characters, keep inherent line breaks, and so on.

Let’s say we have the following code that we want to include in a web page:

<?php function shortLink($atts, $content = null) {
	extract(shortcode_atts(array(
		"href" => 'http://' // default URL
	), $atts));
	return '<a href="'.$href.'">'.$content.'</a>';
}
add_shortcode('link', 'shortLink'); ?>

After converting the “<” characters to their encoded equivalents, “<”, we would wrap the code in <pre> tags and get this in the browser:

<?php function shortLink($atts, $content = null) {
	extract(shortcode_atts(array(
		"href" => 'http://' // default URL
	), $atts));
	return '<a href="'.$href.'">'.$content.'</a>';
}
add_shortcode('link', 'shortLink'); ?>

Looks pretty similar, eh? Notice that the line breaks, spacing, and unencoded characters (besides opening brackets) That’s the whole point: the <pre> tag enables you to easily display raw chunks of source code, text (think poetry), and any other content that we don’t want the browser to process. Now let’s look at that same code when displayed without <pre> tags:

‘http://’ // default URL
), $atts));
return ‘‘.$content.’‘;
}
add_shortcode(‘link’, ‘shortLink’); ?>

As you can see, when not wrapped in <pre> tags, code examples such as this are inaccurate and essentially useless. <pre> elements ensure that the content retains its “natural” format, which is crucial for displaying code, poetry, equations, and other types of specialized content.

Also, note that the <code> element is frequently used in conjunction with the <pre> tag when using syntax highlighters and formatting plugins such as the excellent Code Auto Escape. For example, here at Perishable Press, all multi-line code examples are wrapped with <pre><code> and then auto-escaped using the Auto Escape plugin, which automatically escapes all characters within <pre> elements, eliminating the need to manually convert, say, opening brackets to their encoded equivalents. Many other syntax highlighters and code-formatting plugins also provide this functionality, greatly facilitating the post-writing process. Such tools help you streamline production and improve display of your preformatted content.

One last thing about the <pre> tag before we dive into the good stuff. You should keep in mind that <pre> tags are used for displaying blocks of preformatted characters. That is, regardless of how many lines of text or code you use, the <pre> tag is a block-level element and will display its content as such. Conversely, the <code> tag is an inline element that is perfect for displaying individual words, characters, and lines of preformatted text within a block-level element. For example, I use the <code> tag all the time — just check out the source code of the previous couple of sentences to see what I mean.

Displaying preformatted content & dealing with overflow

Default behavior: horizontal scrollbars

One of the biggest challenges to displaying preformatted content using the <pre> tag is dealing with continuous strings of characters such as URLs and long chunks of code. By default, the <pre> element handles this by stretching to fit its content. Assuming that no widths have been specified in the CSS, the <pre> element will expand in length to accomodate its longest line of content. Interesting, but not very practical in most design scenarios. As soon as a constraining width is applied, the <pre> element uses a horizontal scrollbar to accomodate any content that doesn’t “fit” within the display area. This default behavior is deployed in the stylesheet like this:

pre {
	overflow: auto;
	width: 500px;
	}

This is a commonly seen way of dealing with oversized <pre> content. It’s fast, easy, and effective. Even so, not everyone likes to scroll..

Word-wrapping

Instead of using a scrollbar to display long lines of preformatted text, some prefer to wrap the content within the display area of the <pre> element. When we wrap preformatted content, any lines that extend beyond the specified width will wrap to the next line. As simple as this sounds, some browsers have real problems when it comes to continuous strings of text such as long URLs. Many browsers are unable to break up continuous lines of text unless specifically instructed to do so. Fortunately this is possible using a crefully crafted set of CSS directives.

To enable word-wrapping for your <pre> content, use the following slice of CSS and edit the first directive to set the desired width:

pre {
	width: 500px;                          /* specify width  */
	white-space: pre-wrap;                 /* CSS3 browsers  */
	white-space: -moz-pre-wrap !important; /* 1999+ Mozilla  */
	white-space: -pre-wrap;                /* Opera 4 thru 6 */
	white-space: -o-pre-wrap;              /* Opera 7 and up */
	word-wrap: break-word;                 /* IE 5.5+ and up */
	/* overflow-x: auto; */                /* Firefox 2 only */
	/* width: 99%; */		       /* only if needed */
	}

Once in place, this code will force your <pre> areas to stay at 500 pixels wide. If any continuous lines extend beyond this width, they will be broken and wrapped accordingly.

Auto-expanding code box with CSS

If word-wrapping isn’t for you, you may want to implement some auto-expansion functionality. Using a small snippet of CSS, it is possible to style your <pre> tags such that they magically expand whenever the user hovers their mouse. So, for example, your <pre> areas would display normally at 500pixels in width, but as soon as the user hovers their cursor over the area, it would instantly expand to a larger width, say, 700 pixels.

Setting this up is easy. Just include the following CSS in your stylesheet and enjoy the results:

pre {
	overflow: auto;
	width: 500px;
	}
pre:hover {
	position: relative;
	width: 700px;
	z-index: 99;
	}

Of course, you will want edit the width values according to your needs and customize your <pre> and hover styles to look all sweet. You know.

There are some pros and cons to this method. Here are some of the pros:

  • It works great without requiring any JavaScript.
  • Scrollbars appear only when the box is not expanded.
  • The boxes expand instantly — no waiting required.
  • Works fine even without an inner <code> wrapper.

..and of course some of the cons:

  • All <pre> tags expand even if it’s not necessary (i.e., the code fits)
  • The sudden display change may disorient/confuse newer web users.
  • Expanding box may interfere with other page elements.
  • Even after expansion, the <pre> are may still require horizontal scrollbars to see all of the content.

Auto-expanding code box with jQuery et al

The CSS expanding-box method works nice, but it’s a bit choppy — upon hover, it’s like, BAM — suddenly you’re staring at a full-size code box. Using a little bit of jQuery, we can create a much smoother expanding code box, as originally shared by Chris Coyier at Digging into WordPress.

The jQuery method is an improvement over the CSS method, behaving more elegantly:

  • Expands only when hovered over
  • Expands only as wide as necessary
  • Expands with some nice animation

To implement, begin by styling the <pre> element with a little CSS:

pre {
	overflow: auto;
	width: 500px; 
	}

This will prepare the <pre> element for the following JavaScript:

$(function(){
	$("pre").hover(function() {
		var codeInnerWidth = $("code", this).width() + 10;
		if (codeInnerWidth > 500) {
			$(this).stop(true, false).css({zIndex:"99",position:"relative",overflow:"hidden"}).animate({width:codeInnerWidth+"px"});
		}
	}, function() {
		$(this).stop(true, false).animate({width:500});
	});
});

And that’s pretty much it. You’ll want to customize the CSS and jQuery to fit your needs, but the main thing is getting all of the widths to match up (both in the CSS and JavaScript). When JavaScript is unavailable on the user’s browser, this method degrades gracefully to default <pre> elements with auto-scrollbars.

As discussed at Digging into WordPress, this method requires that your preformatted content is wrapped in both <pre> and <code> tags. For complete information, check out the the original article.

Whipping scrollbars into shape

Vertical scrollbars, height, and Internet Explorer

If you do decide to use scrollbars to display overflow content, you will discover many inconsistencies across browsers, especially (surprise surprise) Internet Exploder. The first thing to understand is that, on standards-compliant browsers (i.e., anything other than IE) vertical scrollbars will only appear if an explicit height is specified for the <pre> element. If it is, vertical scrollbars will appear whenever the preformatted content contains more lines than is viewable within the specified height.

Why is this important? Because it makes eliminating vertical scrollbars rather easy. As a general rule of thumb, I never specify heights for preformatted content, but there are situations where you might want to do so.

As for our ‘ol friend IE, you are pretty much going to get vertical scrollbars regardless of whether or not you declare a height. They just appear on their own. Like friends of that squatter you mistakenly let stay with you for awhile.

Fortunately, there are effective ways to exterminate those stinky vertical scrollbars in IE. Here is the method I use on many of my sites:

/* no vertical scrollbars for standards-compliant browsers */
pre {
	overflow: auto; 
	width: 500px;
	}
/* no vertical scrollbars for IE 7 */
*:first-child+html pre {
	padding-bottom: 20px;
	overflow-y: hidden;
	overflow: visible;
	overflow-x: auto; 
	}
/* no vertical scrollbars for IE 6 */
* html pre { 
	padding-bottom: 20px;
	overflow: visible;
	overflow-x: auto;
	}

That code is pretty much plug-n-play with only the essentials, but you will want to customize the width in the first declaration block and add other styles as desired. What’s happening with this code? Glad you axed. Lemme break it down:

  1. first block — sets default scrolling for standards-compliant browsers; no vertical scrollbars unless explicit height is set.
  2. second block — hack for IE 7 that removes the vertical scrollbar and adds a bit of padding to make room for any horizontal scrollbar that might be present.
  3. third block — hack for IE 6 that removes the vertical scrollbar and also adds some padding.

Of course, rather than using unsightly CSS hacks for IE, it’s best practice to summon them via conditional comments. Once in place, this code will neutralize and eliminate those nasty vertical scrollbars in IE and provide some extra bottom-padding to make room for horizontal scrollbars when they appear. This code is effective, but the additional padding is not needed when the content fits within the <pre> area and the horizontal scrollbar does not appear, resulting in an awkward blank line. Fortunately we can turn to JavaScript to account for this dynamic display property.

Fixing IE’s funky inside scrollbars with JavaScript

IE is so bad..” — how bad is it? It renders horizontal <pre> scrollbars on the inside of the element. This sucks because it interferes with content, pushes everything up about 20 pixels, and invokes display of the unnecessary vertical scrollbar.

We saw how to remedy this display deficiency using CSS in the previous section, but it wasn’t quite ideal because of the extra bottom padding that is used to ensure content display when horizontal scrollbars appear. A better way to handle it is to use a little JavaScript to do the following:

  1. Check if the browser is Internet Explorer
  2. Find all <pre> elements with overflowing horizontal content
  3. Add 20 pixels of bottom padding to account for the horizontal scrollbar
  4. Remove the vertical scrollbar

Thankfully, Remy Sharp delivers this functionality with the following slice of JavaScript:

window.onload = function () {  
	// only apply to IE  
	if (!/*@cc_on!@*/0) return;  
	// find every element to test  
	var all = document.getElementsByTagName('*'), i = all.length;  
	// fast reverse loop  
	while (i--) {    
		// if the scrollWidth (the real width) is greater than 
		// the visible width, then apply style changes    
		if (all[i].scrollWidth > all[i].offsetWidth) {
			all[i].style['paddingBottom'] = '20px';
			all[i].style['overflowY'] = 'hidden';
		}
	}
};

..and as if that weren’t cool enough, here’s the same functionality via jQuery:

(function ($) {
	$.fn.fixOverflow = function () {
		if ($.browser.msie) {
			return this.each(function () {
				if (this.scrollWidth > this.offsetWidth) {
					$(this).css({ 'padding-bottom' : '20px', 'overflow-y' : 'hidden' });
				}
			});
		} else {
			return this;
		}
	};
})(jQuery);
// usage
$('pre').fixOverflow().doOtherPlugin();

Either of these methods will do the job nicely, making IE appear to display any horizontal scrollbars on the outside of the <pre> element.

Interestingly enough, we can also execute this dynamic functionality from within the stylesheet and eliminate the need for any JavaScript. By using one of IE’s proprietary expression properties, we can include the following directly in our IE-only stylesheet:

pre {
	width: 500px;
	overflow-x: auto;
	overflow-y: hidden;
	padding-bottom: expression(this.scrollWidth > this.offsetWidth ? 20 : 0);
	}

Just specify the width and you’re off to the races. For either of these dynamic methods (i.e., JavaScript, jQuery, or CSS expression), keep in mind that we are treating horizontal overflow issues when an explicit vertical height has not been specified.

Bonus tip: displaying a name for each class of pre text

If you have different types of preformatted content, you can output the name of each one within the content itself. To illustrate, let’s say you provide different types of code snippets at your site, such as CSS, HTML, and PHP. By adding a class to an inner <code> element, you can combine CSS-2.1’s attribute selector and :after pseudo-element selector to display the class name within the preformatted content area.

pre code[class]:after {
  content: 'highlight: ' attr(class);
  display: block; text-align: right;
  font-size: smaller;
  padding-top: 5px;
}

Then, you set up your preformatted code like so:

<pre><code class="CSS">

The class name you specify for each type of code block will then be displayed to the bottom-right of the preformatted content area. Shouts out to Chris Coyier for this awesome trick.

Styling preformatted content

Now that we know how to improve the physical properties of the <pre> element, let’s wrap things up with a few ideas for styling the actual preformatted content.

Fonts

When styling your preformatted content, employ fonts suitable to its purpose. For example, if your site is mostly sporting code snippets, throw down with some tough monospace typography:

  • monospace
  • "Courier new", Courier, "Andale Mono", monospace
  • Consolas, "Lucida Console", Monaco, monospace
  • Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace

To create the perfect monospace font stack, you really should be testing on as many different browsers as possible. Check different operating systems, browsers, zoom settings, and types of preformatted content. A couple of notes: Courier is an incredibly tight-looking font that, unfortunately, does not scale well on all browsers/systems. Particularly, there seems to be a lower-limit to the display size of the Courier font. Also, to get things looking right on both PC and Mac without all the fuss, simply declare “monospace” for your <pre> font and you’ll get a great-looking font on both systems.

Borders

While maybe not beneficial to every block of preformatted text, a nice set of borders can really help accent and emphasize your content. Especially for code snippets, adding a border around the area can help users discern easily between it and the post content.

The are many cool things that can be done with <pre> borders:

  • Place a border on only one or two sides of the <pre> text
  • Use varying widths and/or styles (e.g., dotted or custom image)
  • Get all “Web-2.0” and round your corners

Colors

Colors are another inspiring thing to play with when it comes to styling the presentation of your <pre> elements. You can color the text itself, the background, and the borders. Anything is possible here, I just wanted to point out that a good combination of colors for these different properties can really help your <pre> areas “pop.” Also fun to play with when choosing the perfect color combination is transparency, which might be used to let the background show through just a bit. Background images also present unlimited possibilities, such as adding a personal logo/icon or even a nice drop shadow for the upper/left border.

How I roll..

Last but not least, here is the template snippet that I use as a base for styling <pre> tags. After splicing this little snippet into my stylesheet, I proceed to tweak and fuss ‘til everything’s just right..

code, samp, kbd {
	font-family: "Courier New", Courier, monospace, sans-serif;
	text-align: left;
	color: #555;
	}
pre code {
	line-height: 1.6em;
	font-size: 11px;
	}
pre {
	padding: 0.1em 0.5em 0.3em 0.7em;
	border-left: 11px solid #ccc;
	margin: 1.7em 0 1.7em 0.3em;
	overflow: auto;
	width: 93%;
	}
/* target IE7 and IE6 */
*:first-child+html pre {
	padding-bottom: 2em;
	overflow-y: hidden;
	overflow: visible;
	overflow-x: auto; 
	}
* html pre { 
	padding-bottom: 2em;
	overflow: visible;
	overflow-x: auto;
	}

Rather than another long-winded diatribe exploring the manifold intricacies and subtleties of this particular stylistic strategy, I defer to your currently established comprehension of CSS and invite subsequent analysis of its constituent declarations. Or, in the parlance of the day: “it’s plug-n-play, dude – I’m outta here!!!

Hungry for more

As mentioned, I post a lot of code here at Perishable Press. So much so, that I have become rather obsessed with the fine art of formatting and styling preformatted content. As tweeted the other day, I could spend all day just fussing over <pre> code. To me, it really is that important. In this article, I have shared many different techniques for creating perfect <pre> tags, but these methods are far from exhaustive. I am always on the lookout for new and useful ways to improve the appearance and functionality of my preformatted code examples, so if you happen to know any sweet tricks that I missed, share them and I will update the article with the method and link to your site.

About the Author
Jeff Starr = Web Developer. Book Author. Secretly Important.
USP Pro: Unlimited front-end forms for user-submitted posts and more.

35 responses to “Perfect Pre Tags”

  1. Thomas Scholz 2009/11/09 8:38 am

    Always add class="notranslate" to your pre elements. If you don’t, most translation tools will destroy the content for their users.

    Oh, and Courier is a bitmap font on windows, which can’t be anti-aliased. I don’t even mention it in my stylesheets.

  2. Jeff Starr 2009/11/09 9:36 am

    Hi Thomas, thanks for the tip on the notranslate attribute. Gonna have to implement that one. I wonder how many translations have been mangled..

    As for Courier, one of the reasons why is it so tight and crisp is precisely because it’s a bitmap font that can’t be anti-aliased. Anti-aliasing improves display only around 70% of the time.

  3. Thomas Scholz 2009/11/09 1:23 pm

    Courier is in Windows available in 10px, 12px and 15px only. What happens if you declare 11px is (as I read it) undefined by the CSS specification.

    Another problem is the user’s minimum font-size (mine is 14px, but I know people with 18px). A bitmap font looks ugly in the best case here, unreadable in the worst case.

    Undefined cases (like :first-letter on li) may be sometimes quite interesting, but in a tutorial? I don’t know ;)

  4. Dave Doolin 2009/11/09 11:00 pm

    Man, this is sublime. Rich. Too much to take in right now…

    I’m currently using a WordPress plugin for formatting code. It works ok, but conflicts with Thesis theme css. Would like to move to a better plugin, haven’t taken the time to look. Definitely appreciate the effort you put into this.

  5. Bindass Delhiite 2009/11/11 9:16 am

    That’s awesome.

    Nicely detailed and explained.

    CSS is awesome and jQuery rocks.

  6. Absolutely my pleasure, Dave. You know I live and breathe this stuff ;)

  7. Oh my gawd, Jeff. You’ve outdone yourself yet again! Your article reminded so much that the webdesign community seriously need somebody like you to write an all-emcompassing tutorial on tackling naughty and tricky HTML elements.

    <pre> tag is always a pain in the butt to me because it’s not an easy thing to style. Overflows (i.e. pre { overflow:auto; }) are buggy in Internet Explorer and sometimes I have to resort to using not-so-elegant javascript to fix the scrolling problems in IE :/ that was until CSS3 was widely accepted in browsers, making it possible to target overflowing along a single axis, by using pre { overflow-x: auto; overflow-y: hidden; }. That worked!

    Two thumbs up for including the hover-expand tutorial from Chris! I’ve been looking for it forever and I lost it when my Firefox profile got corrupted months ago (now I’m smart – I rely on Delicious and Xmarks *chuckles*).

    Jeff, thank you so much for giving a wonderful writeup on how to tackle the tag. It’s amazing that you’re still able to churn out quality articles even though you’re so busy!

    p/s: I think it’s getting really cold over there now, isn’t it? I was reminded of your decision to move to your new house during spring/summer and not during winter. Smart choice!

  8. ebo. seagrave 2009/11/17 9:32 pm

    You are a master. I will be reading more from you. Thanks so much for this.

  9. Thanks, It helped me a lot to design and add style to http://info.w3calculator.com, I do my own coding and your style helped me doing it.

  10. Really superb. Bravo!

    I did’nt find the solution to my problem but I really enjoyed reading this article. I gonna come back for further readings. I came here since I was looking how to apply the pseudo-element :before and :after on tags. The idea is to use googleon and googleoff instructions on contents. Like this:

    pre:before { content: "<!--googleoff: all-->" } pre:after { content: "<!--googleon: all-->"; }

    For some reasons I don’t get it doesn’t work. Since it is a stupid idea? Or just my code won’t work?

    Any idea dear reader?

  11. @Philippe: on the contrary, that’s an excellent idea. You will probably need to escape the characters with their hexadecimal Unicode character equivalents. But even then, Google is not going to see it because they are looking at your markup and not your CSS styles. Very innovative idea though :)

  12. An excellent stupid idea!!! I frequently use pseudo:before and its brear pseudo:after and forgot it is not printed in the source code. :-D :-D :-D

    Silly me! I’ll back to post some more stupid questions.

    Take care Jeff.

Comments are closed for this post. Something to add? Let me know.
Welcome
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
The Tao of WordPress: Master the art of WordPress.
Thoughts
I disabled AI in Google search results. It was making me lazy.
Went out walking today and soaked up some sunshine. It felt good.
I have an original box/packaging for 2010 iMac if anyone wants it free let me know.
Always ask AI to cite its sources. Also: “The Web” is not a valid answer.
All free plugins updated and ready for WP 6.6 dropping next week. Pro plugin updates in the works also complete :)
99% of video thumbnail/previews are pure cringe. Goofy faces = Clickbait.
RIP ICQ
Newsletter
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.