Considerations for styling the < pre > tag
You’ve probably used it. It’s that very special tag in HTML that allows for the white space within the tags to actually be honored. For example, four spaces will actually be four spaces! That’s unlike how HTML normally works, where white space “collapses” (the four spaces will become one). The <pre>
tag is useful indeed.
Do you use the <code>
tag inside?
The “pre” of a <pre>
tag literally means “preformatted text” – which doesn’t say anything about what that text is. A <code>
tag, semantically, says the text within is code. Makes sense to me! I always use it when placing blocks of code, which in my experience is the #1 use case.
<pre><code>
function cool(x) {
return x + 1;
}
</code></pre>
Quick aside: Notice there is a line break before the text starts in the block above. That line break will render, which can be highly annoying. There is no great CSS way to handle that. The best way is just to start the text on the same line as the <pre>
tag, or programmatically remove the leading white space.
Picking a font
Since the primary use case of the <pre>
tag is blocks of code and code is generally written in a monospace font, setting a monospace font-family
is probably a good idea.
Lucky for us, the “user agent stylesheet” (the styles you get in the browser without adding any of your own CSS at all) already sets font-family: monospace;
. So, you could just do nothing at all. Or, you could get fancy.
There is an article from 2009 by Michael Tuck who explored “font stacks”. That is, listing a bunch of fonts in a single font-family
declaration such that the most ideal choices come first, and fall down the stack toward less ideal choices. His example stack for monospace fonts takes cross-platform pre-installed fonts into play:
font-family: 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;
I’m not sure how that stack still holds up across all platforms today, but it looks like a great start.
Alternatively, you could load up your own custom @font-face
font and use that (font stacks still apply). Or, use a service. Typekit has 23 monospace font offerings, as I write this.
To wrap or not to wrap?
This is rather a personal preference. Personally, I’m split.
When coding in my code editor, I prefer it when long lines that break the width of the visible area break down onto the next line down (rather than cause horizontal scrolling). When looking at code in articles, I prefer the code doesn’t break. Weird, I know. On CodePen, we make it a user option since the world is so divided on what they prefer.
When styling, you’ll have to make the choice. If you go with wrapping, fortunately, you can maintain the <pre>
tag’s unique ability to preserve whitespace and get wrapping, like this:
pre {
white-space: pre-wrap;
}
If you go without wrapping, you don’t have to do anything. Except, you should consider what happens in the case of really long lines. Really long lines will happily bust out of fixed width containers or stretch the width of containers unexpectedly. To prevent that, I’d suggest at least:
pre {
overflow-x: auto;
}
You might even consider a max-height
and total overflow: auto;
if you want to avoid obnoxiously tall blocks of code.
Perhaps make it auto-expanding
Some people, perhaps even you, dislike both line wrapping and horizontal scrolling. There may be a solution! You can keep your <pre>
blocks the default block-container width, but allow them to expand when interacted with:
pre:hover,
pre:focus {
width: min-content;
}
Will this ever be going into an email?
Perhaps some way or another, the HTML you write ends up used in an email. <Pre> tags can be dangerous in email, as your CSS doesn’t apply to emails (which can help the lines wrap), so the default non-wrapping text happens and long lines can break email layouts.
On CSS-Tricks, back when I was auto-generating the email newsletter from the RSS feed, I had to generate a special RSS feed that would process the HTML and ensure that inline styles were forced upon all <pre>
tags like this:
<pre style="white-space: pre-wrap;"></pre>
That way I was doing all I could to ensure blocks of code with long lines wouldn’t break the layout.
Do you need syntax highlighting?
There is no shortage of syntax highlighting options out there. You can web search around for them. Personally, I’m a fan of Prism.js as…
- It’s small in file size;
- It has no dependencies;
- It has sensical class names; and
- It allows you to customize a copy with just the stuff you need.
The only thing I’d give up Prism.js for is some kind of clever way to inject the <span>
s (used for the coloring) server side instead.
Do you label the language?
I personally like seeing blocks of code identified with the language that they are.
Like this:
One way to do that is to label it using a data-* attribute (perhaps one that your syntax highlighter already requires) and then display that, like:
<pre data-lang="HTML"><code>
<h1>Example code</h1>
<code></pre>
pre[data-lang]::before {
content: attr(data-lang);
display: block;
}
I don’t think that’s a particularly accessible way to do it, so perhaps someone can chime in in the comments about that. Maybe a title
attribute would be better?
Controlling the spacing
If you use actual tab characters in the blocks of text within <pre>
tags (not just multiple spaces that look like tabs), you might be surprised at how wide those tab characters render.
Tabs render as 8 spaces wide by default, ludicrously enough.
Seems like 4 spaces is more normal in coding environments. Fortunately, you can control it your liking:
pre {
tab-width: 4;
}
Personally, I like spaces anyway ;).
Other options
It’s not a trivial amount of effort to get code blocks displaying nicely on a site, but very doable. If you’d rather leave the job to someone else, CodePen offers Embedded Pens that can showcase blocks of code nicely (along with a preview), and embedded GitHub Gists are also popular.