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.

Screen Shot 2016-05-21 at 9.02.25 AM.png

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;

1697-monostack.thumb.jpg

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.

linewrapping.gif

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;
}

overflow.gif

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;
}

hover.gif

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…

  1. It’s small in file size;
  2. It has no dependencies;
  3. It has sensical class names; and
  4. It allows you to customize a copy with just the stuff you need.

Screen Shot 2016-05-21 at 9.22.31 AM.png

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:

Screen Shot 2016-05-21 at 9.23.39 AM.png

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.

tab-size.gif

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.

Comments

  • tab-size! How has this been hiding from me for so long?!

    My most common use case for the pre element is song lyrics. Older drafts of the W3C spec used poetry as the primary example for the pre element. I make heavy use of it on my band’s website to mark up our charts. Superscript and some creative positioning allows you to display chord changes above the words and syllables where they go: https://bottomdwellersmusic.com/setlist/chart/110/

  • Is it tab-size or tab-width? The picture does not match the example, currently. :)

    • chriscoyier

      Proof in the pudding! It’s `tab-size` – I’m sure the post can be updated.

      • karlis upitis

        Hi, Chris! Excellent article!
        Are you using some kind of polyfill for tab-size support on IE? http://caniuse.com/#feat=css3-tabsize
        I tried to use it but ended up with Js solution.

  • Taylor Hunt

    For accessibility, I think <figure>:

    <figure>
    <figcaption>HTML<figcaption>
    <pre><code>...</code></pre>
    <figure>

    • emerazea

      Good call.

      HTML5 Doctor concurs with you as well.

    • That said, this comments section really needs some better colors for code. Dark blue on dark grey is not a great choice.

    • For accessibility, consider advising users to just use the appropriate heading level. Title attribute is way out, and while screen readers now recognize generated content, I think most developers understand headings and can just drop it in *before* the pre element. It is also relative easy to style and degrades well.

  • I prefer to indicate a language as in this example:


    <pre><code class="language-pascal">
    var i: Integer;
    begin
    i := 1;
    end.
    </code></pre>

    https://www.w3.org/TR/html5/text-level-semantics.html#the-code-element
    https://html.spec.whatwg.org/multipage/semantics.html#the-code-element


    class="language-xxxx"

    Also, it is used by Prism.js.

  • I’ve often wondered about the use of <pre> and <code> together – why both? Why not just <code>? (or <code class=”multiline”> for blocks) or something? Using <code> implies preformatted text anyway, making the <pre> seem a bit redundant.

    And honestly, <pre> in general seems redundant in much the same way <font> does. If you want poetry, why not <p class=”poetry-verse”> or <section class=”poem”> ?

    • chriscoyier

      One potential issue: then you’re 100% reliant on your CSS to make the code look as you intend (by setting the white-space). If the content goes anywhere else besides your website (apps, rss, email, etc), that formatting will be off.

  • chriscoyier

    re: https://twitter.com/mathias/status/735825660623622144

    Sounds like it’s best to use `class=”language-*` to label languages, as the spec says: https://html.spec.whatwg.org/multipage/semantics.html#the-code-element … which is what Prism.js wants anyway, so, win.

    <pre><code class="language-pascal">var i: Integer;
    begin
    i := 1;
    end.</code></pre>

  • Seriously though, is there *anything* that IE actually supports? 😀

  • I have another use case for Pre that feels a bit sketchy, but “doing it the right way” would be too complicated: Formatting white space filled blobs from a database.

    I’ve styled a front-end for a couple sites that got data that was entered in a textarea. Users were making “faux paragraphs” by just making line returns. The database was saving this space, but of course not adding P tags. To preserve the white space we styled a Pre tag (we made the font match the site theme etc). It felt a tiny bit wrong, but seemed to solve all our problems.

    I did an accessibility test: http://codepen.io/ArleyM/pen/qObeRw
    And I wrote more about this hack here: http://arleym.com/pre-element-use-case-brilliant-or-hacky

Related Articles