Speaking Plainly in HTML

As I was thinking about writing a couple of articles, I noticed I wasn't really looking forward to writing HTML by hand. I mean... it's not that bad... but I guess it's unappealing enough that there are certainly a lot of tools out there to help avoid it: static site generators, special editors, content management systems, frameworks, and so on.

I thought on it some and I figured maybe I could make it more pleasant by keeping the document structure simple and avoiding verbose code. I thought a simple document structure would be less mentally taxing and less code would be less tiring to my fingers.

I set out to see how simple I could make the document structure and how sparse I could make the code. Bearing in mind, of course, that a document must serve its purpose (not the other way around), so I couldn't leave out anything necessary to the commercial or artistic purposes of the document or anything whose absence would unduly impair the usability of the document.

Happily for this experiment, the articles I was preparing to write did not have any special commercial or artistic requirements; I just thought it would be nice to share some recent experiences and thoughts.

I don't propose to convince you that you should author all of your documents using exactly the tactics I describe below, but I would like to share my experience in the hope that it might encourage you to follow a similar strategy of questioning the assumptions you may have about what a good HTML document requires and developing tactics suitable for the work you're doing.

For the purposes of this experiment, I tried to set aside what I knew of best practices for preparing HTML documents. I figured that many or most of these were probably meant to cope with the complexity or verbosity I was trying to reduce and that it was probably worth re-thinking them in this new context.


There are many examples on the web of documents that look clean and simple when rendered in a user agent, but these documents often implement this visual simplicity with a great volume of CSS and a complex document structure to support it.

For this experiment, I tried to avoid overriding the user-agent's base style sheets. And where I did use CSS, I tried to avoid complicating the document structure to support it. Aside from serving my goals of reducing code size and complexity, this also left me with more time and attention for the content itself.

I would encourage you to test the assumptions you have about how much styling your documents need. Of course, if the document is in part an artistic expression or if it has particular commercial requirements, then you've got to do what you've got to do. But it may be worth checking what those requirements really are and how they would be best served.

There's plenty of slick-looking junk on the Internet, but documents prepared by domain experts or enthusiastic hobbyists often have very good content with limited or outdated styling. I think many readers pick this up as a signal that a document that feels too slick might not have trustworthy content. You may find that being economical in the styling of your document sends a positive signal to your readers about the quality of your content.

Minimal document

I knew a conforming document would need some minimal amount of code to support it. I was surprised after reading the spec how little is needed. A document type declaration is required, along with the title tag. The html, head, and body tags can be omitted and the user agent will understand them to be implied as long as certain reasonable requirements are met to avoid ambiguity.

It's also necessary to specify the character encoding, but this need not be done with a meta element: a content-type header from the server or a UTF byte-order mark (BOM) at the beginning of the document is also acceptable. I often use Sublime Text, which includes an easy way to save UTF-8 with a BOM (File → Save with Encoding → UTF-8 with BOM). I chose this method since it eliminates a tag that doesn't really add to the document (all conforming documents are UTF-8) and doesn't rely on server configuration to serve conforming documents.

A minimal conforming document, then, can be as short as:

<!DOCTYPE html>
<title>Test document</title>

Though this leaves some usability opportunities. In particular, I wanted my documents to work well on mobile devices as well as personal computers. The document above fails on both points: on most mobile devices it is rendered too small to be readable without zooming in while on personal computers the measure of the text will likely be too wide for comfortable reading.

For mobile devices, I chose to include viewport metadata to advise the user agent that the page should be rendered at normal scale. Without this, most mobile devices will render the document into a viewport larger than the device's screen and scale it down. With the metadata, they should render the document at normal scale and infer a more suitable viewport.

For personal computers, I chose to set a maximum width on the body element to limit the measure of the text for ease of reading. The usual guidance is 20 to 40 em. I went to the wider end of this range since it seemed to help the prose fit well with my other content.

I settled on this for a minimal conforming, usable document:

<!DOCTYPE html>
<meta name=viewport content="initial-scale=1">
<title>Test document</title>
<body style="max-width: 40em">

I'm familiar with using indentation and matched tags for helping manage the complexity of typical HTML documents. I wasn't planning to make complex documents for this experiment and I was hoping to leave out any tags I could, so I thought I'd try something different: I used vertical whitespace to organize logical parts of my code rather than indentation. I found I could disable automatic indentation in Sublime Text by opening the HTML syntax specific settings (Sublime Text → Preferences → Settings → Syntax Specific) and adding "auto_indent": false to the root JSON object.

Now it just remained for me to fill this document up with interesting content. The HTML specification describes a great number of elements, but I tried to use those that were semantically appropriate, that had suitable default styling, that had short tags, and whose end tags could be omitted. I couldn't help but notice that these are mostly older elements. I suppose the early designers of HTML may have meant to make it easy to compose documents by hand.


I found that most of my documents had a natural structure that resembled a tree. For these, I used the section heading elements in their usual way. Their semantics are well-defined and they are important to creating an outline of the document. I found their default styling very suitable: I thought it made their purpose easily understood by the reader.

I wanted to put some contact information at the bottom of each of my articles. The address element turned out to be a good fit for this. Given the name of the element, I was a little surprised by the semantics, but it turns out they were just right. I was satisfied with the default styling. It doesn't provide its own margins, but I was nestling mine between a couple of paragraphs anyway, so it worked out fine.

So far, my documents have been simple enough that I haven't had much need for elements like article, section, nav, aside, and so on.


For prose, the paragraph element was indispensable. It has suitable default styling and is semantically applicable to a wide variety of uses. Its start tag is as short as possible and its end tag can be omitted in most circumstances.

I found myself using the code element here and there as well to provide a distinct visual style for certain computery stuff. I tried not to overdo it, though. I tried to use it mainly where the content wouldn't otherwise flow well with the surrounding text due to abbreviations, punctuation, or awkward grammar and also where I thought it would be helpful to call out to the reader precisely the characters I had typed or which were shown by the computer.

In the document source, I chose to hard-wrap my lines at eighty characters — probably just a personal peculiarity. Sublime Text has a wrapping function built in which is designed to re-wrap a group of lines that is separated from the rest of the file by blank lines and this worked well with my strategy of using vertical whitespace to organize my code.

I ended up with something like this:

<h1>Test document</h1>

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec justo
felis, imperdiet nec gravida non, dapibus quis eros. Pellentesque vehicula
tortor nec ex feugiat, ut pellentesque risus maximus.

<p>Nam nec ante in ipsum posuere lobortis. Quisque porttitor vulputate
mauris elementum malesuada. Duis consectetur dignissim tortor, eget dignissim
sem sagittis id.

I was happy because most of the resulting code was little more than the content I wanted to write, with just a light sprinkling of tags.


A hypertext document isn't much without links. Aside from those necessary for basic navigation, I tried to include links in my documents that I thought I would find helpful if I were the reader.

Where possible, I tried to work links into prose. In some cases, this would have been forced or awkward and I ended up putting them in a list (as on an index page, for example). I found some use for links in tabular data too, providing a way to download the raw data files that supported each row in the table.

Long URLs didn't always want to wrap nicely with my other text. I usually don't put spaces around the equals sign between an attribute name and its value (just habit, I guess) but in a few cases I added one to let just the href value wrap to the next line to get a little relief. The awkwardness of working long addresses into my text has perhaps biased me in favor of shorter URLs and fewer links. For security reasons, I believe many user agents no longer allow URLs that have been split over multiple lines.

A paragraph with a couple of links might have come out like this:

<p>Lorem ipsum dolor sit amet, ad purto tollit reformidans quo, ei eum cetero
dissentiunt. Per tale vituperata te, <a href=nostrum.html>vulputate</a>
ullamcorper te pro. Tempor cetero <a href=
voluptaria-duo-ne-mea-ea-virtute-suscipit-luptatum>perfecto pertinax mei in</a>.
Mea ludus homero singulis et, posse tantas ridens nam ex.

Pre-formatted text

I found that several of the documents I was preparing would benefit from the inclusion of console output, configuration files, and the like. For this sort of pre-formatted text, I found the pre element very handy. On mobile in portrait mode, forty character lines seem to fit alright with default styling (everything old is new again). In landscape, eighty character lines seem to fit fine. On the desktop, I found that eighty character lines fit pretty well with the 40 em measure I had selected too.

With default styling, the pre-formatted text will simply overflow the body if necessary. This allows for horizontal scrolling in most user agents, which seemed to be a usable compromise for eighty character lines in portrait orientation on mobile.

Lorem ipsum dolor sit amet, ad purto tollit reformidans quo, ei eum cetero
dissentiunt. Per tale vituperata te, nostrum vulputate ullamcorper te pro.
Tempor cetero voluptaria duo ne, mea ea virtute suscipit luptatum, perfecto
pertinax mei in. Mea ludus homero singulis et, posse tantas ridens nam ex.


I didn't find a use for lists in every document, but where I did need them they were just the right thing. In particular, on an index or index-like page I found an unordered list perfect for giving titles of and links to a group of related documents. The closing tag for list item elements can be omitted in cases where it doesn't cause ambiguity.

<li><a href=lorem>ipsum dolor sit</a>
<li><a href=amet>ad purto tollit</a>
<li><a href=reformidans>quo ei eum cetero</a>

On one page, I had a long list of documents and wanted to give some particulars about each one. I ended up using a definition list with the document title as the “term” and each detail about the document as a separate “definition.” The spec is very flexible about how terms and definitions match up. As with ordered and unordered lists, the closing tags for term and definition elements can be omitted in most cases.


<dt>HP 35665A Installation and Verification Guide
<dd>Part number 35665-90029
<dd>Describes how to install the instrument and perform operational and
performance verification tests.

<dt>HP 35665A Quick Start Guide
<dd>Part number 35665-90035
<dd>Introduces the controls of the instrument and walks through some example
tasks to get the user comfortable making measurements.

<dt>HP 35665A Concepts Guide
<dd>Part number 35665-90028
<dd>Provides a conceptual overview of the instrument and its features. This
is the main document to read for learning how to use the instrument.



In a few of my articles, I thought it would really be helpful to include scans of output from lab instruments and photographs of test setups. For these, I used the image element. I tried to think of the images as part of the prose and this seemed to work pretty well: it helped inform where and how the images should be included in the document and also helped guide how the alternative text should be written.

The images I was using seemed to contain enough detail and encapsulate complete enough ideas to stand on their own as paragraphs. Images can also be run inline with text, but — setting aside the typographical trickiness — I didn't find much use for this in the documents I was preparing. So, I wrapped my images each in their own paragraph tag.

I found that an image width of 600 pixels seemed to fit well with a measure of 40 em (the default font size for most user agents is 16 pixels; multiplying by 40 em gives a measure of 640 pixels). I tried to always remember to set the height and width attributes so the page could be laid out in its final form even if the images had not yet loaded.

To provide a good experience for readers with high DPI displays, I scaled my images to 1200 pixels wide. I tried also to take some steps to reduce the size of the image files to reduce time they would take to load and to ease bandwidth usage. I recognize the conflict between serving double-sized images indiscriminately and wanting to reduce bandwidth usage. I think that it is principally mobile device users who benefit from both high DPI images and lower bandwidth requirements, so it seems like a bit of a conundrum. Aside from which, it seems like solutions for adaptively serving different image files are rather complex right now.

For most of the images I used, it was easy to write alternative text: I wanted it to flow with the surrounding prose and support it as well as possible in the same way that the image would if it were visible. I did have a couple of tricky cases where the images were not purely decorative but were meant to provide flavor and context by juxtaposition with the other document elements (what the spec seems to call “ancillary images”). In these cases, I tried to capture in the alternative text the same flavor provided by the images rather than simply providing a purely objective account of their content. I think this exercise helped me evaluate where and how I used images in my documents and aided me in producing a more deliberate arrangement.

For photographs, I chose to use JPEG encoding because the quality factor can be adjusted to reduce file size quite a bit without causing offensive visual artifacts. I used JPEGOptim to strip metadata, optimize the Huffman tables, and bring down the quality factor to 85%. For photographs straight out of my camera, I got an 85% size reduction by scaling down to 1200 pixels wide and a further 8% reduction cropping to my preferred aspect ratio (3:2). A trip through JPEGOptim gave a further reduction of 45%.

For scans of charts and text, I found I preferred PNG for getting a crisp representation of the source material. For these images, I was able to get file sizes down by reducing the color depth: one-bit black and white was fine for most of the scans while indexed mode with a small palette seemed more suitable for others that had been annotated in color. Again here, I chose to strip metadata in the interest of smaller files. Going from color to grayscale on one of my scans resulted in a size reduction of 14%. Thresholding and dropping to a 1-bit map with no dithering gave a further reduction in size of 63%. A trip through PNGCrush shrunk the size by another 8%.

For images that were to be converted to black and white, I chose to set the threshold at a value I found produced good output with my material and I also turned dithering off. Usually I think it would help represent gray regions or soft edges, but my source material wasn't meant to have either so I figured it would look better without.

For images I wanted to retain some simple color information in, I chose a palette of 16 colors after some experimentation. Here I left dithering on, but I'm not sure if it made a lot of difference either way.

For elements with many attributes, like the image element, I found it helpful to split the opening tag across several lines. In the case of long attributes — like the alternative text — I also sometimes would split an attribute value across multiple lines.

<p><img src=ts9-dibuf2.png width=600 height=500
alt="A power spectrum plot shows a large peak at the fundamental frequency
and small peaks at the second and third harmonics. A relative marker shows the
second harmonic 640Hz up in frequency and 40 decibels down in power from
the fundamental.">

The spec allows that quotes around an attribute value are optional as long as their omission doesn't cause ambiguity and the value doesn't include whitespace. I mostly left them off where I felt comfortable doing so.

Images comes out to a bit higher code-to-prose ratio than plain text and preparing the files themselves took some work, but I thought it a reasonable trade-off for the value they brought to my documents.


In a couple of my articles, I had some data to share that really seemed to lend itself to a tabular presentation. I used the table element and its friends for this. This is a place where the default styling let me down a little. Without adding some styling, I didn't think the table structure was as clear as I would like. There used to be a simple attribute for table borders that probably would have suited my use case, but it has been deprecated. I figured I could get what I needed with some combination of whitespace and/or borders. I ended up leaning more on borders than whitespace since some of the data I wanted to share was rather dense. I put the styling in the head element and let it apply to all tables in the document since I had several tables to share and wasn't using the element for anything else. The CSS looked something like this:

table, th, td {
	border: thin solid;
	border-collapse: collapse;
	padding: 0.2em 0.5em;

I tried to arrange my data so that the width of the tables would fit in with the text around them. This is another case where horizontal scrolling in portrait mode on mobile felt like a reasonable compromise: I think if someone were to read the particular documents in question one-handed on a mobile device, they'd probably skip right past the tables and look for a summary in the text.

Here's an example of a small table from one of my documents:


<th>Tone control
<th>Lower corner
<th>Upper corner
<th>Peak response





In one case, I had a three sets of data for similar measurements taken under different conditions. I chose to share this as three separate tables, each with a caption to describe the condition under which the measurements were taken. I was a little surprised to find that the content model for the caption element is flow content (except other tables). I had imagined it only being good for a short label or description, but it's more flexible than that. I put my caption text in a paragraph to get reasonable whitespace around it.

<caption><p>Drive at midrange</caption>


<!-- And so on... -->



I didn't get real deep into forms, since I was mostly just looking to share some information. But I did have some documents where I wanted to provide a way to sign up for email notifications of new articles.

I was pleased to find that the spec suggests simple paragraphs for layout out form controls. I followed this suggestion and was delighted to find that the resulting code was simple and that the form fit in nicely with the rest of my document. I did end up making a concession at my mailing-list host's recommendation to include an invisible div to help mitigate bot traffic. We do what we must because we can.

I thought it handy to label my input fields, so the user would know what I hoped for them to input. The label element can be associated with a labelable element either by using its for attribute or by enclosing the labelable element in the label element itself. Because of the simplicity and proximity of my labels and inputs, I found it easy to enclose inputs in their labels.

The mailing list host's example code for the form explicity set the form input values to the empty string. The spec says you can give an attribute a value of the empty string implicity by giving only the attribute name or explicitly by giving both the attribute name and a value of the empty string. However, an unquoted attribute value cannot be the empty string. I found that the spec also says that if the value attribute is not set on an input, the default value of the input is the empty string. I'm not sure if the developer of this example code knew of a buggy browser that needed this attribute set or if they were simply exercising an overabundance of caution. If you know, please drop me a line; I'd love to include the answer to the mystery here.

<form action=subscribe method=post target=_blank>

<div style="position: absolute; left: -5000px;" aria-hidden=true>
<input type=text name=bot_trap tabindex=-1>

<label>Email Address: <input type=email name=EMAIL></label>
<input type=submit value=Subscribe name=subscribe>


Audio and video

In the course of this project, I didn't encounter a need to share audio or video content. But, I have on other projects and I hope I'll have an opportunity in the future to dive into the relevant specs and see if I can find a nice way to code it up and have it fit in with the style and tactics that I developed for this project.

Other thoughts

I made notes on a number of small topics as I worked. Where possible, I tried to fold them into the broader subjects above, but a few defied my attempts. I've collected them here in the hope that they might be of some value.


For one of the sites I was working on during this project, I thought it would be nice to include a Favicon. I didn't want to fuss with including metadata in each document for the more modern touch icons and such, but I figured it wouldn't be too much hassle to drop a favicon.ico file in the root directory.

I spent a fair bit of time researching all the best advice on what sizes to include in the ICO file and in the end ignored all of it and settled on just 64x64. I checked the browsers I had handy and it seemed to work fine in all of them and it scaled up okay on iOS too. It's not optimal for every case, but it's a reasonably small file (about 2.5kB for mine) and it's easy to make. I used 16 colors because it worked fine for my material, but if you need 256 colors the file will come out larger.

Facebook and sharing

For some of the documents I was working on, I didn't have any expectation that they would be shared via social media so I didn't take any steps to optimize for that. The big players seem to make some effort to do something reasonable with documents that don't offer them any special affordances and I figured that would be sufficient for those documents in the unlikely event that someone wanted to share them there.

On the other hand, I was also preparing some documents that I figured would be shared primarily through a handful of Facebook groups. For these, I did make some effort to optimize for sharing. I focused on two areas: meta elements and an image. I only looked at Facebook for this project, but I think the process is similar for the other major platforms.

The Open Graph protocol has a list of “required” properties. These are required for conformance, but not necessarily for sharing to work well. Facebook offers a nice sharing debugger that will show you what your document will look like when shared and will offer suggestions on how to improve the metadata. As of this writing, it also provides the benefit of caching your document's image so the first person to share the document will see it when composing their message.

Speaking of images: Facebook seems to use the first image in the document if no contrary guidance is given in the metadata. They seem to crop the image to a two-to-one aspect ratio. I pre-cropped my images at about three-to-two as a compromise between what Facebook wanted and what I thought looked good in the document. The debugger was great for previewing the results.

I haven't yet added a button or link to my documents for direct sharing on Facebook, but I believe it's possible to use a link with redirection rather than bringing Facebook's scripts into the document. URL redirection is discussed in the Share Dialog documentation. I'll probably check it out if and when I'm ready to sign up for an application ID, which seems to be required.


I had a couple of documents in which I wanted to provide a way for folks to pay me via PayPal. PayPal has something called a “hosted button” that you can set up in their selling tools. The website code they provide has a (low resolution) button image and some form stuff I wasn't real excited about. However, they also provide email code that's just a regular URL. I found I could use this as a regular link in my document just fine.


All conforming documents are UTF-8, which I felt free to take advantage of where I thought it could make my life better. Emoji seem to tweak the line-height, so I tried to use those sparingly. But I found other so-called “special characters” to be a great improvement over HTML entities.

I didn't bother trying to use curly single quotes as apostrophes because I use a lot of them and I knew I would be inconsistent or rephrase to avoid them. I don't use as many double-quotes, though, so I tried to remember to curlify them as I went along. I suppose some straight ones snuck through. I learned to type on a typewriter and old habits are tough to break.

On a Mac, you can type Option-[ for an opening double quote and Option-Shift-[ for a closing double quote. You can also get an en-dash with Option-Hyphen or an em-dash with Option-Shift-Hyphen. Matthew Butterick has nice pages about hyphens and dashes and straight and curly quotes.

There are other Unicode characters you might find handy. I like using little arrows for showing menu sequences, for example. On a Mac, Control-Command-Space brings up the Character Viewer with a search box that usually finds me what I'm looking for.


For this project, I chose not to override the user agent's default typeface. I recognized that this would get me Times New Roman or something like it on most platforms. I was happy with that: I think it's a nice typeface, in spite of some folks saying it's overused. I find it pleasant to read and unobtrusive. I think if the worst thing you can say about something is that it's overused, that's not too bad.

I do think that sans-serif typefaces are usually more pleasant to see and probably easier to read on low-resolution displays than serif typefaces. Thankfully, the proportion of low-resolution displays seems to be shrinking daily.

Had I a particular reason to, I would not hesitate to specify a web-safe typeface or a font-stack with reasonable fallbacks. On the other hand, I would probably shy away from bringing in a “web font” due to the technical and mental overhead. Over the course of this project, I found myself coming around more and more to the idea that being too picky about how user agents present my content goes against the grain of the web as a medium and that I will be happier if I go with the grain.

Applying styles

For styles that would only apply to a single element, I chose to attach them as an attribute of that element (thinking that this proximity would reduce cognitive overhead when writing or modifying the document).

<body style="max-width: 40em">

For styles that would apply to many similar elements, I chose to use a style sheet embedded in the document (thinking that this de-duplication would reduce the verbosity of the document).

body {max-width: 40em}
table, th, td {
	border: thin solid;
	border-collapse: collapse;
	padding: 0.2em 0.5em;

If I end up with important styles that should apply site-wide or at least to a great number of documents, I may promote them to their own CSS file to be included in each document. I'll have to figure out the right balance between verbosity and cognitive simplicity. Then too, if there's a lot of styling it may noticeably improve the user's experience to make it cacheable. I hope not to produce that much styling for most of my projects.


I found that the simplicity and small size of the documents I produced as part of this experiment gave me a great starting point for performance. However, I still found some places for improvement. I got some great insights from WebPagetest, especially about the value of optimizing images (which I wrote about at length in that section). The results I got from that tool also have prompted me to look into what I can do to improve DNS and TLS performance. With everything else tuned pretty tight, those items now stand out as a significant part of the time required to fetch and render my documents.

Further reading

I'm sure I can't remember all of the great resources I used or documents I read that helped me out on this project — and inspired it — but I do have a small collection of links that I found particularly valuable.


I hope that you've found this account of my journey valuable and that it might prompt you to review your own assumptions about how simple an HTML document can be and to question requirements that might not really be required at all.

If this is the kind of thing you're into, you may enjoy my other articles. If you have any questions or comments, please feel free to drop me an e-mail.

Aaron D. Parks