StyleMap: HTML Visual Sitemap Tool
Update! I've released a new version of this technique that has more simplified markup structure and better browser support. Please read Stylemap v2 HTML visual sitemap.
If you've ever had to produce a sitemap for a client using tools like Visio or OmniGraffle, you know that it can be tedious to visually organize a complex hierarchy of pages. Having experienced this myself, I developed a simple and efficient way to make a sitemap through the benefits of standards-based HTML and CSS.
StyleMap uses valid HTML, CSS and a touch of DOM scripting to produce a visual sitemap out of an embedded definition list. The result is a functional, scalable, and above-all rapid way to produce a tree-style map for the planning stages of your project.
Keep in mind that although we'll cover the details of how the sitemap works, in practice you'll only need to worry about changing the markup to match your website structure. The CSS and Javascript can then be added as they are and will take care of the rest!
Advantages of this method
- StyleMap uses simple HTML lists, making for easy updates, as opposed to desktop alternatives that use proprietary file formats.
- The presentation is driven by CSS, allowing many possibilities to match the look and feel of your client's branding.
- The entire tree scales beautifully with browser text-resizing.
- StyleMap degrades gracefully for browsers without support for CSS, images, or Javascript.
- Anchor elements within each list item allow them to be linked to their corresponding page.
- As opposed to a typical PDF, an HTML sitemap allows it to be indexed by search engines and accessible to multiple devices.
Why definition lists?
We've all seen numerous uses of DL's for cases stretched far beyond a simple term and definition. But element naming aside, we only have so many types of lists in our HTML toolbox, so we make do with what we've got. In my opinion, the definition list provides a more realistic and semantic relationship to a sitemap than does a UL or OL. This is because in a DL, the term (DT) is not only a heading of the definitions (DD's), but also a sibling of them. This relationship seemed a perfect fit to the metaphor of a landing page and the other top-level pages in its section. By contrast, an ordered list (OL) would require for each embedded OL to be a child element of an LI, meaning it would be one level deeper than its "landing page"... so to speak. The purist in me feels justified.
So how does it work?
First step: The markup structure
We start out with a DL with one DT (the homepage) and several embedded DD's to represent the sections of the site. As in a real website, these sections will double as the landing page of their section (ie: /aboutUs/index.html). The sections are named "About US", "News & Events", "Portfolio", and "Contact Us" in the following markup.
<dl>
<dt><a href="#">Homepage</a></dt>
<dd><a href="aboutUs.html">About Us</a></dd>
<dd><a href="newsEvents.html">News & Events</a></dd>
<dd><a href="contactUs.html">Contact Us</a></dd>
</dl>
A small leap: The completed markup
Now that we have our sections, we need to add their inner pages. To do this, we'll have to embed a new list inside each DD, so our current titles will become DT's within each of these DL's. Each section's pages can now be populated as DD's, following immediately after their landing page. For styling purposes, I've added an ID of "sitemap" to the first DL and a class of "root" to its first child DT (the homepage). The sitemap is now complete and ready to style! The completed markup is as follows:
<dl id="sitemap">
<dt><a href="#">Homepage</a></dt>
<dd>
<dl>
<dt><a href="aboutUs.html">About Us</a></dt>
<dd><a href="company.html">Company</a></dd>
<dd><a href="staff.html">Staff</a></dd>
<dd><a href="location.html">Location</a></dd>
<dd><a href="careers.html">Careers</a></dd>
</dl>
</dd>
<dd>
<dl>
<dt><a href="newsEvents.html">News & Events</a></dt>
<dd><a href="pressReleases.html">Press Releases</a></dd>
<dd><a href="eventsCalendar.html">Events Calendar</a></dd>
<dd><a href="archives.html">Archives</a></dd>
</dl>
</dd>
<dd>
<dl>
<dt><a href="portfolio.html">Portfolio</a></dt>
<dd><a href="recentProjects.html">Recent Projects</a></dd>
<dd><a href="caseStudies.html">Case Studies</a></dd>
<dd><a href="clients.html">Clients</a></dd>
</dl>
</dd>
<dd>
<dl>
<dt><a href="contactUs.html">Contact Us</a></dt>
<dd><a href="feedback.html">Feedback</a></dd>
<dd><a href="staffIndex.html">Staff Index</a></dd>
<dd><a href="directions.html">Directions</a></dd>
</dl>
</dd>
</dl>
The fun part: applying the CSS
The CSS needs to meet three needs in particular:
- The map needs to start at the top center and branch outward and downward like a family tree.
- Each fork in the tree must be illustrated with lines connecting at their center points.
- Each item must clearly distinguish itself from other items in the tree.
Stacking the items in their place
For starters, we'll float all of the dl's left. This will create a massive jumble, so to clean it up we'll have to clear all of the DT's left. Example 1 displays the effect of these two rules alone - we're on our way!
dl {float: left;}
dt {clear: left;}
We want the pages of each section to stack next to one another. To do this we'll need to float the nested DD's left: dd dd {float: left;}. We'll also want each landing page to be centered over its sibling pages, which is done simply by adding body {text-align: center;}. Example 2 demonstrates the effect. *Note: Due to the flexible nature of floating all of the DL's, I've added a wide div around the sitemap to keep it from breaking apart.
Boxes and lines
The boxes are created by giving each anchor a 1px solid black border. The horizontal tree branches will be created with a bottom-border on each DT. To keep the landing pages from spanning the entire width that their sibling pages create, we'll give it a fixed width and center it. In example 3, a little extra styling has been added for visual appeal.
The vertical lines will be added to the DT's and the nested DD's by adding a 1x1px black gif background centered and repeated vertically. Example 4 shows the map really starting to take shape.
dt, dd dd {background: url(vLine.gif) 50% repeat-y;}
Transitioning from crossing lines to "trees"
By now you've noticed a problem with the lines: you can see them behind the anchors, and they extend farther than they should (shown in fig 1a). The former can be solved by simply giving the anchors a solid background color (done), But we still need a way to hide the portions of the lines that extend beyond their last intersection. Since the problem only occurs in the first and last item of each list, we'll need to target these elements exclusively with CSS. Due to the lack of hooks (class's, ID's, etc) in this markup, we'll need to make use of some pseudo-selectors, in particular ":first-child" and ":last-child". Figure 1b illustrates our goal.
- Fig 1a: The problem

- Fig 1b: The goal

The magic eraser
To cover up the extended lines, first we'll need to give each DD and DT a -1px top margin. As source order insists, these elements will already be at a higher z-index than their parents, so now the child overlaps its parent DTs' bottom border by 1px. Our child elements are in place, and we can change their background images to a 999px wide gif with a solid black pixel in the center. The image for the :first-childs will have a solid white left half and a transparent right half. The :last-childs' image will be the opposite (left half transparent, right half solid white). Since the background images are centered, their outer halves will cover their parent DTs' bottom border and cut it off where it intersects with the vertical line. The steps below illustrate the process.
The Payoff... sorta
Example 6 shows our functional sitemap, but we've got a little problem: certain browsers don't understand :first-child and :last-child (ie: IE). To achieve a cross-browser sitemap, we'll have to figure out a way to do it without using those cool pseudo-selectors.
A classy workaround
To simulate the :first-child, :last-child targeting, we can apply classes "first" and "last" to the first and last child DD's of each DL. We can then use these classes as hooks to target the elements as we did with the pseudo's. The following markup shows how this looks.
<dl id="sitemap">
<dt class="root"><a href="#">Homepage</a></dt>
<dd class="first">
<dl>
<dt><a href="aboutUs.html">About Us</a></dt>
<dd class="first"><a href="company.html">Company</a></dd>
<dd><a href="staff.html">Staff</a></dd>
<dd><a href="location.html">Location</a></dd>
<dd class="last"><a href="careers.html">Careers</a></dd>
</dl>
</dd>
<dd>
<dl>
<dt><a href="newsEvents.html">News & Events</a></dt>
<dd class="first"><a href="pressReleases.html">Press Releases</a></dd>
<dd><a href="eventsCalendar.html">Events Calendar</a></dd>
<dd class="last"><a href="archives.html">Archives</a></dd>
</dl>
</dd>
<dd>
<dl>
<dt><a href="portfolio.html">Portfolio</a></dt>
<dd class="first"><a href="recentProjects.html">Recent Projects</a></dd>
<dd><a href="caseStudies.html">Case Studies</a></dd>
<dd class="last"><a href="clients.html">Clients</a></dd>
</dl>
</dd>
<dd class="last">
<dl>
<dt><a href="contactUs.html">Contact Us</a></dt>
<dd class="first"><a href="feedback.html">Feedback</a></dd>
<dd><a href="staffIndex.html">Staff Index</a></dd>
<dd class="last"><a href="directions.html">Directions</a></dd>
</dl>
</dd>
</dl>
Example 7 shows the markup above in action. A new CSS file has been created to address the new classes. The result is a cross-browser sitemap that still abides by all of our rules. Unfortunately, all of those classes leave a lot of room for error; if you forget to add just one of the classes, the map will be disconnected. Just one more step will lead us to a practical solution.
Have your cake and... let DOM scripting take it from here
The final step is to keep our initial markup and let Javascript add all of the classes for us. If you're interested in how this is done, you can view the script with notes included at the top. *Note: One thing to mention is that for deeply nested DD's (think family tree), we'll need to add an additional class to cancel some double-padding issues that occur. Luckily, the script adds these classes for us as well, so don't sweat it.
The final product
The following examples show possible implementations for StyleMap. An additional script has been added to detect the width of the map and assign it as a style so that it can be centered in the window. Feel free to turn off CSS, Images, and/or Javascript to see how it degrades somewhat gracefully. Enjoy!
- Clean and Functional: A basic implementation of a website structure.
- A Step Further: A deeply-nested map with color-coding for each level of hierarchy.
Download StyleMap Zip
Final Thoughts
Moving forward, there is always room for improvement. StyleMap could probably be built without images by adding an extra span above each anchor spanning 50% of the width, with a side border and either solid or transparent background as needed. StyleMap also tends to show a stray pixel or two of detached horizontal line in certain browsers, but they don't seem to get in the way of the functionality. Due to the width of the map, I question if it could be used as the sitemap on a live website, or if it is strictly an IA planning tool. Regardless, I hope you find it to be as useful as I have.
Usage
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License.




