How to view source with pride

Or: How I learned to accept Master Pages, but leave them as quickly as possible

Content management systems are good for many things, but outputting nicely indented markup is rarely one of them.

You know the feeling, don't you? You spend all your time crafting templates full of lean, semantic, future-friendly markup, only to view source on the final site and discover HTML that appears to have been thrown together by chimps with shovels.

This happens even with Umbraco, which, unless you do something wrong, gives you complete control over your markup. Alas, this simply appears to be the nature of template-driven content management.

But as proud craftsmen of the web we shouldn't settle for second-rate output from a first-class tool, so let's set out to right this wrong.

Empty lines? We don't need no stinkin' empty lines

Do a quick view-source on your latest Umbraco project. I bet you'll find that the page starts with a blank line before the doctype, right? And ends with another blank line at the end of the document. Important? Hardly (though a dangerous step towards triggering quirksmode in IE). Pretty? Not really.

It's easy to fix, though. A linebreak after the opening or before the closing <asp:Content> tag will output a blank line in your final markup, so you simply need to concatenate the lines in your Master Page file like this:

<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %>
<asp:Content ContentPlaceHolderID="ContentPlaceHolderDefault" runat="server"><!DOCTYPE html>
[insert greatness here]
</html></asp:Content>

How to avoid blank lines in Master Pages

Yes, you're sacrificing proper indentation of your Master Page file, but you're gaining proper formatting of your site's HTML and the respect of all your source-viewing nerd friends. Worth it, right?

Flush left or flushed down the drain

So let's get out of this Master Page and into some macros, shall we? Easy:

<html>
  <head>
    <umbraco:Macro Alias="Head" runat="server"/>
  </head>
  <body>
    <div class="wrapper">
      <umbraco:Macro Alias="Header" runat="server"/>
      <div id="content" role="main">
        <asp:ContentPlaceHolder Id="mainContent" runat="server" />
    </div>
  </body>
</html>

A simple template with macro tags

Yeah, you wish it were that easy, don't you. View source again, and this is what you get:

<html>
  <head>
    <title>A content page | Best Website Ever</title>
<meta name="description" content="" />
<meta name="keywords" content="" />
  </head>
  <body>
    <div class="wrapper">
      <header role="banner">
<a href="/" title="Home" accesskey="1">Best Website Ever</a>
</header>
      <div id="content" role="main">
        <h1>Here is my content</h1>
<p>But oh wait, why is it suddenly indented all wrong?</p>
<p>Argh, I fail at teh internets!</p>
      </div>
    </div>
  </body>
</html>

A simple template... with borked indenting

Oh the pain. The first line of your macro is indented perfectly, but your Master Page will be damned if it cares about the rest of your content.

The solution is to keep all tags in your Master Page flush left. Since this is at odds with truly perfect indenting of your markup, you'll do best to have as little markup in your Master Page as possible and keep the rest in your macros:

<html>
<head>
<umbraco:Macro Alias="Head" runat="server"/>
</head>
<body>
<div class="wrapper">
<umbraco:Macro Alias="Header" runat="server"/>
<div id="content" role="main">
<asp:ContentPlaceHolder Id="mainContent" runat="server" />
</div>
</body>
</html>

Keep all tags flush left in your Master Pages

Remember that all nested ContentPlaceHolder templates must also concatenate lines to avoid blank lines in the output:

<asp:Content ContentPlaceHolderId="mainContent" runat="server"><umbraco:Macro Alias="SubNavigation" runat="server"/>
<umbraco:Macro Alias="Article" runat="server"/></asp:Content>

Remember to avoid blank lines in nested Master Pages as well

Indent? Yes. How can you even ask?

After having whipped our Master Page into shape, let's not let XSLT ruin the party. Thankfully, XSLT was built to create nicely formatted SGML output, as long as we remember to ask politely. You've probably seen this line at the top of all XSLT files that you create via Umbraco's backend:

<xsl:output method="xml" omit-xml-declaration="yes"/>

An XSLT output tag without explicit indenting instructions

This instructs the XSLT processor to output well-formed XML, but it is missing a crucial attribute. Change it to this:

<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>

A simple attribute gets you nicely indented XSLT output

...and you've told the XSLT processor that it may add additional whitespace to your output, which in most cases will mean that the processor will do what it can to nicely indent your output.

If we apply these tricks to our page from before, we'll end up with something more pleasing to the eye:

<html>
<head>
  <title>A content page | Best Website Ever</title>
  <meta name="description" content="" />
  <meta name="keywords" content="" />
</head>
<body>
<div class="wrapper">
  <header role="banner">
    <a href="/" title="Home" accesskey="1">Best Website Ever</a>
  </header>
  <div id="content" role="main">
    <h1>Here is my content</h1>
    <p>And look, my markup is now nicely indented.</p>
    <p>Gee whiz, I might just win at teh internets after all!</p>
  </div>
</div>
</body>
</html>

Behold the final, nicely indented HTML output

So go forth and indent, and take even more pride in the fruits of your labor.

And if you have any tricks of your own for fine-tuning markup, we'd love to hear them in the comments!

Dan Okkels Brendstrup

Dan is on Twitter as