Flag of the United Kingdom
Andreas Rejbrands webbplats

Aktuellt

Web design: Improved markup

As I wrote a few days ago, I have been studying web standards rather intensely during my recreational time this summer, and I have used my new knowledge to improve my web sites. In this article, I’d like to discuss the main improvements.

First, you should notice that most improvements are invisible to the outcome of rendering the HTML in a browser; instead, most improvements are related to the semantics and cleanness of the markup. By ‘cleanness’, I mean that all parts of the markup related to styling the document has been removed to CSS stylesheets. Nevertheless, there have been a small number of visual improvements.

Now, let’s dive into the details. First, as I wrote, I have separated contents (HTML markup) and presentational information (CSS stylesheets) perfectly. Now the HTML markup only contains the information of the document, and makes absolutely no assumptions about the way the document should, can, or may be styled. Actually, my sites were rather good in this respect already before this work began, but I have removed a number of <p>&nbsp;</p> previously used to add vertical space. Now this is done using CSS margins.

Turning to the general structure of each page, I have been thinking in terms of the HTML5 semantics. Hence, each document consists of DIVs with different IDs: header, toolbar, mainContents, and footer. When the time comes and I move to the HTML5 doctype, I might change these to the new semantic HTML5 elements (<header>, <nav>, <main>, <footer>). Previously I used classes instead of IDs for these DIVs. The new use of IDs feels more appropriate since these elements occur exactly once per page. Notice that the ID toolbar of the main navigation bar is a leftover from the very first version of my current site design from the early 2000s. This strange ID will (obviously) vanish when I upgrade to the HTML5 elements.

The navigation bar was previously marked up using

<div class="toolbar">
<a href="default.asp" class="tlbLnk">Main Page</a> |
<a href="biography.asp" class="tlbLnk">Biography</a> |
<a href="ideas.asp" class="tlbLnk">Philosophy</a> |
<a href="documents.asp" class="tlbLnk">Writings</a> |
<a href="applications.asp" class="tlbLnk">Software</a> |
<a href="nature.asp" class="tlbLnk">Photos</a> |
<a href="news.asp" class="tlbLnk">News</a> |
<a href="guestbook.asp" class="tlbLnk">Guest Book</a> |
<a href="contact.asp" class="tlbLnk">Contact Me</a> |
<a href="links.asp" class="tlbLnk">Links</a>
</div>

This has been changed to

<div id="toolbar">
<ul>
<li><a href="default.asp">Main Page</a></li>
<li><a href="biography.asp">Biography</a></li>
<li><a href="ideas.asp">Philosophy</a></li>
<li><a href="documents.asp">Writings</a></li>
<li><a href="applications.asp">Software</a></li>
<li><a href="nature.asp">Photos</a></li>
<li><a href="news.asp">News</a></li>
<li><a href="guestbook.asp">Guest Book</a></li>
<li><a href="contact.asp">Contact Me</a></li>
<li><a href="links.asp">Links</a></li>
</ul>
</div>

First, notice that the separator character, which was purely presentational, has been removed from the markup, as required by the principles. Also notice the semantic use of the unordered list. Notice that the classes on the links have been removed. This is because I don’t want to repeat myself (DRY); it is already apparent that the links are toolbar (navbar) links, because they are children of the div#toolbar node in the DOM tree. All the fancy CSS selectors are there to be used! This removal of unnecessary classes in this way has been done in many places. The CSS related to the navbar is

#toolbar {
color: #FFFFFF;
background-color: #000000;
padding: 0;
}
#toolbar ul {
list-style-type: none;
margin: 0;
padding: 0;
background: black;
color: white;
padding: 2px;
font-size: 0px;
}
#toolbar li {
background: black;
display: inline; /* If 'inline-block' is not recognised, like in FF 2 */
display: inline-block;
margin: 0;
padding: 2px 4px;
font-size: small;
}
#toolbar li + li {
border-left: 1px solid gray;
}
#toolbar a {
text-decoration: none;
padding: 2px 5px;
}
#toolbar a:link, #toolbar a:visited {
color: white;
background: black;
}
#toolbar a:hover {
color: black;
background: #CFCFAC;
}
#toolbar a:active {
color: black;
background: #AEAD71;
}

I considered using CSS content to add a vertical line character (|,U+007C: VERTICAL LINE) between the items in the list, but eventually settled for the border approach instead; it looks somewhat better, actually. Since display: inline-block is recognised in IE 7 and some earlier versions, but ignored on elements that are block-level by default, I use an IE conditional comment <!--[if lte IE 7]> to include the following CSS file:

/* This is not a valid CSS stylesheet. However, it conforms to a data format expected by Internet Explorer 7 and earlier. */
/* This file must not be used by a valid HTML document. Of course, it may be 'linked' inside a HTML comment. */

#logobar {
filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=1, StartColorStr='#AEAD71', EndColorStr='#CFCFAC'); /* IE 5.5 to 7 */
/* IE 8 and 9 recognises -ms-filter. IE 10 supports linear-gradient. */
}

#toolbar li {
display: inline; /* IE ≤7 recognises 'inline-block' but doesn't render it correctly for LIs. */
}

Oh, this reminds me of an improvement of the logobar (webpage header). Previously, the logobar consisted of a raster image with the text “Andreas Rejbrand’s Website” on a linear-gradient background. Of course, that sucks! Both text and linear gradients should be done by ‘vector’ means. Now, the markup is simply

<div id="logobar">
<a href="default.asp">
Andreas Rejbrand's Website
</a>
</div>

and the associated style is

#logobar {
font-family: Cambria, Georgia, Serif;
height: 1.5em;
line-height: 1.5em;
font-size: 1.5em;
padding: 0.5em;
background-color: #CFCFAC;
-ms-filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=1, StartColorStr='#AEAD71', EndColorStr='#CFCFAC')"; /* IE 8 and 9 */
background-image: -webkit-linear-gradient(left, #AEAD71, #CFCFAC);
background-image: -moz-linear-gradient(left, #AEAD71, #CFCFAC);
background-image: -ms-linear-gradient(left, #AEAD71, #CFCFAC);
background-image: -o-linear-gradient(left, #AEAD71, #CFCFAC);
background-image: linear-gradient(to right, #AEAD71, #CFCFAC);
}
#logobar a {
color: black;
text-decoration: none;
}
#logobar a:hover {
text-shadow: white 0px 0px 5px;
}

Yes, I couldn’t help adding some visual candy there (:hover). In the block above, you can see the CSS3 way of adding a linear gradient, plus a few vendor-prefix versions conforming to a somewhat older syntax. Unfortunately, MSIE doesn’t support linear gradients in any CSS3-like way before IE10 (almost surprisingly, since IE9, even IE8, is pretty good in most other cases). Instead, in IE8+ you can use -ms-filter. But IE5.5-IE7 only supports the non-conforming unprefixed filter property. Hence, to add the gradient in old IEs, without making the CSS invalid, I put the unprefixed filter version in the IE7-specific stylesheet mentioned previously.

Similar to the main navbar, the pixlinks navbar was previously

<div class="logobar singleBorder" style="padding:8px;">
<strong>Plants:</strong>
<a href="sommar.asp">Nature, summer</a>,
<a href="vinter.asp">Nature, winter</a> |
<strong>Animals:</strong>
<a href="djur.asp">Mammals</a>,
<a href="aves.asp">Birds</a>,
<a href="reptiler.asp">Reptiles</a>,
<a href="leddjur.asp">Arthropods and Molluscs</a> |
<strong>Culture:</strong>
<a href="nyc.asp">New York City (1/2</a>,
<a href="nyc2.asp">New York City (2/2)</a> |
<a href="portratt.asp">Portraits</a> |
<a href="mathart.asp">Mathematical pictures</a>
</div>

That is ugly! First, the separators, “,” and “|” are purely presentational (especially the latter). Second, the CSS class singleBorder was, as the name suggests, purely presentational, and hence it is an invalid class name (classes have nothing to do with styles). Third, inline CSS should only be used in the very rare cases when the style in some sense is very intimately connected with the element, maybe in a semi-semantic way. Fourth, according to the HTML5 semantics, <strong> is used to signify something important, like some particularly important passage in a warning text. It should definitely not be used to mark up some kind of ‘header’, like in this case. It’s plain incorrect. Today, the markup is much improved:

<ul class="two-level-nav">
<li>
<b>Plants</b>
<ul>
<li><a href="sommar.asp">Nature, summer</a></li>
<li><a href="vinter.asp">Nature, winter</a></li>
</ul>
</li>
<li>
<b>Animals</b>
<ul>
<li><a href="djur.asp">Mammals</a></li>
<li><a href="aves.asp">Birds</a></li>
<li><a href="reptiler.asp">Reptiles</a></li>
<li><a href="leddjur.asp">Arthropods and Molluscs</a></li>
</ul>
</li>
<li>
<b>Culture</b>
<ul>
<li><a href="nyc.asp">New York City (1/2)</a></li>
<li><a href="nyc2.asp">New York City (2/2)</a></li>
</ul>
</li>
<li><a href="portratt.asp">Portraits</a></li>
<li><a href="mathart.asp">Mathmematical pictures</a></li>
</ul>

two-level-nav is a site-wide new class that can be used for two-level navigational lists, like this one. Notice that the class describes the semantics of the UL; it is not purely presentational like the ugly old singleBorder. I’d like a list header tag here, but <b> is fine according to the HTML5 semantics. The corresponding style is

/* Two-level nav */

ul.two-level-nav, ul.two-level-nav ul, ul.two-level-nav li {
padding: 0;
margin-left: 0;
margin-right: 0;
}
ul.two-level-nav > li, ul.two-level-nav > li > ul {
display: inline-block;
}
ul.two-level-nav > li > ul > li {
display: inline;
}
ul.two-level-nav li {
list-style-type: none;
}
ul.two-level-nav {
background: #AEAD71;
color: black;
padding: 4px;
}
ul.two-level-nav > li {
background: #CFCFAC;
margin: 0px 0px;
padding: 4px 8px;
transition: background 250ms;
}
ul.two-level-nav > li:hover {
background: white;
}
ul.two-level-nav b:after {
content: ":";
}
ul.two-level-nav ul li:not(:last-of-type):after {
content: ",";
}

One downside of this approach is that the last selector is very fancy, so the commas will disappear already in IE8. Of course, I could have used a simple ul.two-level-nav ul li + ul.two-level-nav ul li:before selector to add a comma followed by a space (this also requires some additional changes (why?)), but that appears somewhat less elegant. Since the loss of the commas isn’t that terrible, I chose elegant code before perfect backwards compatibility in this case. Also notice the nice CSS3 transition.

Turning to the main contents of the web pages, I follow strictly the HTML5 semantics, as described in the previous article. In practice, this means that quite a few <em> has been changed to more suitable elements. Yes, unfortunately, I used the stress emphasis element as a generic italics element, which is a semantic nightmare (almost, at least). Now I strictly use <em> only for stress emphasis, I use <dfn> for defining instances of phrases, <cite> for titles (of books, films, …), and <i> for phrases in a foreign language (together with the lang attribute, of course).

I actually believe that my sites are far better than most on the web today when it comes to semantics.

Further, I have been very careful to markup the (human) language of text. My site is bilingual (Swedish and English). The lang attribute is always set on the <html> element to the main language of the page. I also set the language explicitly on articles (<div class="article" lang="en">), sample grammar boxes, etc., explicitly. Then I use CSS content to add an English flag every time an English article appears on a Swedish page, and vice versa. For example, an article may look like

<div class="article" lang="en" id="ITEM159">
<div class="header">
<h2>Web standards</h2>
<dl class="metadata" lang="sv">
<dt>Notis</dt>
<dd><a href="news.asp?ItemIndex=159">159</a></dd>
<dt>Datum</dt>
<dd>2013-08-21</dd>
</dl>
</div>

<p>During my recreational hours this summer,

...

This article is found on my Swedish site, so an English flag will appear in the article’s header, because the article is in English. But the metadata names are in Swedish, because the article is served on the Swedish site. But the metadata is in the header, which is in the article, so the metadata needs to set the language to “sv” explicitly.

The CSS related to articles is

.article { }
.article .header h2, .article .header p {
margin: 0;
}
.article .header {
color: black;
background: #EEE;
padding: 4px;
border: 1px solid black;
}
.article .header h2 + dl {
margin-top: 1em;
}
.article {
margin-bottom: 4em;
}
:lang(sv) > .article:lang(en) .header h2:after {
content: url("http://rejbrand.se/pix/Flag_of_the_United_Kingdom_small.png");
padding-left: 0.5em;
}
:lang(en) > .article:lang(sv) .header h2:after {
content: url("http://rejbrand.se/pix/Flag_of_Sweden_small.png");
padding-left: 0.5em;
}

Notice that I think in HTML5 terms: Articles and headers.

I am also very careful to set the language correctly even on very small phrases in a foreign language, like in

<p>Jag påverkas också oerhört starkt av vissa filmer. Till exempel såg jag <cite lang="en">Titanic</cite> förra året. Efter filmen vaknade jag mitt i natten, och kände en förtvivlad sorgsenhet. Det var som att all lycka och glädje i världen sjönk med Titanic, och allt som återstod i min värld var tomhet. Ännu värre var <cite lang="en">The Phantom of the Opera</cite> (2004). Det är en alldeles underbar film, dels för musiken och scenerna, men också för filmens oerhörda (psykologiska) verklighet.

Being careful with the language, I suppose, is very important for screen readers.

Naturally, there are quite a few hyperlinks on my English site to my Swedish site, and in the opposite direction. If following a hyperlink will change the webpage language, this is indicated visually:

body:lang(sv) a[hreflang=en]:after {
content:url("http://rejbrand.se/pix/Flag_of_the_United_Kingdom_small.png");
padding-left:0.5em;
}
body:lang(en) a[hreflang=sv]:after {
content:url("http://rejbrand.se/pix/Flag_of_Sweden_small.png");
padding-left:0.5em;
}

Now, return for a while to the article headers. Notice that I use description lists to mark up metadata name–value pairs, as is semantically rich. This is also new: Previously I used (oh, horror) <strong> and <br/> with plain text in a paragraph. Today I use the semantically-named class metadata when a description list is used as a metadata list. The CSS is

dl.metadata {
margin: 0;
}
dl.metadata dt {
float: left;
clear: left;
font-weight: bold;
padding: 0;
margin: 0;
margin-right: 1ex;
}
dl.metadata dt:after {
content: ":";
}
dl.metadata dd {
margin: 0;
padding: 0;
}

I also took the liberty of adding some new visual sugar while updating my sites:

blockquote {
position:relative;
}
blockquote p:before {
color: darkgrey;
display: block;
position: absolute;
font-size: 400%;
left: -30px;
top: -10px;
}
blockquote:lang(sv) p:before {
content: "\201D";
}
blockquote:lang(en) p:before {
content: "\201C";
}

and

img:target, ul:target, ol:target, p:target, div:target,
blockquote:target, table:target {
box-shadow: red 0px 0px 5px;
}

and

code {
background: #EEE;
color: black;
padding: 2px;
display: inline-block;
}
kbd kbd {
display: inline-block;
background: black;
color: white;
font-family: 'Arial Unicode MS', Arial, Helvetica, sans-serif;
border: 0.3em outset gray;
padding: 0.1em 0.3em 0.1em 0.3em;
}
kbd kbd:active {
border: 0.3em inset gray;
}

Let me end this article by giving a more fancy example of CSS content: I do write quite a lot about Swedish grammar on my site, and thus I need textboxes that exemplify both correct and incorrect written text. The markup might look like

<div class="correct-language">
<p>Vi kommer till tre städer: Stockholm, Linköping och Malmö.</p>
</div>

The corresponding style is

:lang(sv) > .correct-language:lang(en):before, :lang(sv) > .incorrect-language:lang(en):before,
:lang(sv) > .possibly-correct-language:lang(en):before {
background-image: url("http://rejbrand.se/pix/Flag_of_the_United_Kingdom_small.png");
background-repeat: no-repeat;
background-position: 90% 50%;
padding-right: 3em;
}
:lang(en) > .correct-language:lang(sv):before, :lang(en) > .incorrect-language:lang(sv):before,
:lang(en) > .possibly-correct-language:lang(sv):before {
background-image: url("http://rejbrand.se/pix/Flag_of_Sweden_small.png");
background-repeat: no-repeat;
background-position: 90% 50%;
padding-right: 3em;
}

.correct-language {
background: #CCFFCC;
color: black;
padding: 0.2em 1em 0.2em 1em;
margin: 1em 0 1em 0;
}
div.correct-language:before {
position: relative;
left: -2em;
top: 0.5em;
font-weight: bold;
background: #4C994C;
color: white;
padding: 0em 1em 0em 1em;
box-shadow: black 2px 2px 5px;
}
div.correct-language:lang(sv):before {
content: "Korrekt";
}
div.correct-language:lang(en):before {
content: "Correct";
}

Of course, I strictly make sure that the type of textbox (like correct or incorrect language) is made clear even with no stylesheet is applied.

The separation of presentational information (style) from markup has allowed me to serve a number of alternate stylesheets the user can choose from. I will talk more about that in a few days.


Visa alla tidigare notiser.

Visa enbart de senaste notiserna.