How to build a threaded comment block with HTML5 and CSS3

Blogs and forums often have threaded comment replies built into their network. Popular social networks Reddit and Hacker News both support threaded comments, which allows users to directly reply inline with comments, voicing an opinion without getting lost in the shuffle.

We’re going to look at building a simple HTML5 and CSS3 threaded comments layout. We won’t be using any jQuery effects on the comment blocks (although it is possible to extend this functionality). We will look into structuring the HTML5 document and how to position elements using CSS. From this base template it should be easy for developers to pick up their own customizations and implement a threaded comment block into any website layout.

Before we get started, take a look at the demo to see what we’ll be building. If you want to, you can download the source from here.

Constructing the webpage header

I want to start by looking over the basic HTML template used in this layout. I am sticking to the HTML5 doctype coded in UTF-8 along with some other HTML5 elements. For web browsers older than Internet Explorer 9 we include a copy of the HTML5shiv script hosted on Google’s dev servers.

<!doctype html>
<html lang="en-US">
<head>
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
 <title>HTML5/CSS3 Threaded Comments Block</title>
 <meta name="author" content="Jake Rocheleau">
 <link rel="shortcut icon" href="https://www.webdesignerdepot.com/favicon.ico">
 <link rel="icon" href="https://www.webdesignerdepot.com/favicon.ico">
 <link rel="stylesheet" type="text/css" href="styles.css">
 <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Wellfleet">
<!--[if lt IE 9]>
 <script type="text/javascript" src="https://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>

This will allow newer elements such as <header>, <footer>, and <section> to render properly in all browsers. The only issue would be running into web browsers with JavaScript disabled, but in my opinion you can’t drive yourself mad catering to every single user. I have included another external resource for the Wellfleet Google webfont which styles the page header text.

(Isn’t it great that we live in a time where all of these resources are free for developers to use? It allows us to rapidly prototype a comments template without too much hassle.)

Inner body content

Inside the <body></body> tags we can find a whole bunch of inner comment divs. The whole page layout is wrapped inside a div with the ID #w for wrapper. Everything is centered on the page with a fixed width of 700px.

The #container div is what holds the comment system in place away from the header text or other page elements. Each thread of comments is held inside an unordered list where the root <ul> has the ID #comments. Each internal element is given the class .cmmnt and replicates much of the same inner content. Here is a sample comment from the page layout:

<li>
<div><a href="javascript:void(0);"><img src="images/pikabob.png" width="55" height="55" alt="pikabob photo avatar"></a></div>
<div>
<header><a href="javascript:void(0);">Pikabob</a> - <span>posted 6 days ago</span></header>
<p>Listen you are going to get a kick out of this one. I've got to tell one of the funniest jokes of all time.</a></p>
</div>
</li>

The inner comment contains a floating avatar image along with another div using the class .cmmnt-content. This inner div container helps to distinguish from the avatar column, so that way our content is placed solely on the right side. This also allows room to place <header> and <footer> tags inside the comment body, which may hold additional links for editing or sharing.

The inner comments use internal unordered lists with the class .replies attached onto each one. This allows us to duplicate comment levels 3 or even 4 threads deep. For this demo I have only created styles to support 3-tier level comments. But it is very simple to customize threads to go even deeper.

<li>
 <div><a href="javascript:void(0);"><img src="images/dark-cubes.png" width="55" height="55" alt="DarkCubes photo avatar"></a></div>
 <div>
 <header><a href="javascript:void(0);">DarkCubes</a> - <span>posted 1 week ago</span></header>
 <p>Ut nec interdum libero. Sed felis lorem, venenatis sed malesuada vitae, tempor vel turpis. Mauris in dui velit, vitae mollis risus. Cras lacinia lorem sit amet augue mattis vel cursus enim laoreet. Vestibulum faucibus scelerisque nisi vel sodales. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis pellentesque massa ac justo tempor eu pretium massa accumsan. In pharetra mattis mi et ultricies. Nunc vel eleifend augue. Donec venenatis egestas iaculis.</p>
 </div>
 <ul>
 <li>
 <div><a href="javascript:void(0);"><img src="images/pig.png" width="55" height="55" alt="Sir_Pig photo avatar"></a></div>
 <div>
 <header><a href="javascript:void(0);">Sir_Pig</a> - <span>posted 1 day ago</span></header>
 <p>Sed felis lorem, venenatis sed malesuada vitae, tempor vel turpis. Mauris in dui velit, vitae mollis risus.</p>
 <p>Morbi id neque nisl, nec fringilla lorem. Duis molestie sodales leo a blandit. Mauris sit amet ultricies libero. Etiam quis diam in lacus molestie fermentum non vel quam.</p>
 </div> 
 </li>
 </ul>
</li>

Notice that even the lower-level threaded replies still follow the exact same formatting as top-level comments. This is obviously helpful if you are wrapping these styles around a CMS such as WordPress. Then you can dynamically generate comments to fill in the same template.

CSS Stylesheet resets

I have created another document, styles.css, which holds all our basic CSS code for positioning and designing page content.

I’m starting off with my own customized CSS resets which are based on Eric Meyer’s code snippets. This will reset all fonts, margins, padding, and more importantly box-sizing for all elements on the page in every major browser. You may also notice I am using some properties for dynamically updating the CSS highlight color.

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
 margin: 0;
 padding: 0;
 border: 0;
 font-size: 100%;
 font: inherit;
 vertical-align: baseline;
 outline: none;
 -webkit-box-sizing: border-box;
 -moz-box-sizing: border-box;
 box-sizing: border-box;
}
html { height: 101%; }
body { background: #e3e0ef url('images/bg.png'); font-size: 62.5%; line-height: 1; font-family: Arial, sans-serif; padding-bottom: 65px; }

::selection { background: #d7d0f3; }
::-moz-selection { background: #d7d0f3; }
::-webkit-selection { background: #d7d0f3; }

article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; }
ol, ul { list-style: none; }

blockquote, q { quotes: none; }
blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; }
strong { font-weight: bold; } 

table { border-collapse: collapse; border-spacing: 0; }
img { border: 0; max-width: 100%; }

As we move further down I have included code for resetting the basic styles on h1 elements and paragraphs in the page. We need to setup a core structure for the wrapper and container divs. On the container I have actually included a few CSS3 box shadow properties to stand out from the background.

h1 { font-family: 'Wellfleet', 'Trebuchet MS', Tahoma, Arial, sans-serif; font-size: 2.85em; line-height: 1.6em; font-weight: normal; color: #756f8b; text-shadow: 0px 1px 1px #fff; margin-bottom: 21px; }

p { font-family: Arial, Geneva, Verdana, sans-serif; font-size: 1.3em; line-height: 1.42em; margin-bottom: 12px; font-weight: normal; color: #656565; }

a { color: #896dc6; text-decoration: none; }
a:hover { text-decoration: underline; }

/* page layout structure */ 
#w { display: block; width: 700px; margin: 0 auto; padding-top: 35px; }

#container { 
 display: block; 
 width: 100%; 
 background: #fff; 
 padding: 14px 20px; 
 -webkit-border-radius: 4px; 
 -moz-border-radius: 4px; 
 border-radius: 4px; 
 -webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.3);
 -moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.3);
 box-shadow: 1px 1px 1px rgba(0,0,0,0.3);
}

Styling the page comments

Now this last bit of code to look over will setup all our comments on the page.

#comments { display: block; }

#comments .cmmnt, ul .cmmnt, ul ul .cmmnt { display: block; position: relative; padding-left: 65px; border-top: 1px solid #ddd; }

#comments .cmmnt .avatar { position: absolute; top: 8px; left: 0; }
#comments .cmmnt .avatar img { 
 -webkit-border-radius: 3px; 
 -moz-border-radius: 3px; 
 border-radius: 3px; 
 -webkit-box-shadow: 1px 1px 2px rgba(0,0,0,0.44);
 -moz-box-shadow: 1px 1px 2px rgba(0,0,0,0.44);
 box-shadow: 1px 1px 2px rgba(0,0,0,0.44);
 -webkit-transition: all 0.4s linear;
 -moz-transition: all 0.4s linear;
 -ms-transition: all 0.4s linear;
 -o-transition: all 0.4s linear;
 transition: all 0.4s linear;
}

#comments .cmmnt .avatar a:hover img { opacity: 0.77; }

To start each .cmmnt list item is designed to be pushed over to the left with each internal <ul> container. I have thepadding-left: 65px; value setup so that each block is pushed aside regardless of length or height. Each avatar image is positioned absolutely to the left side, so having that additional padding keeps content away from the edge.

I also applied some unique CSS3 border-radius effects and transitions onto the avatar images. As you hover over each one the opacity will be gradually reduced to 77% over 400 milliseconds. This is a very interesting effect to have with only a few short lines of code. It used to require lengthy JavaScript to even replicate something close to this transition.

#comments .cmmnt .cmmnt-content { padding: 0px 3px; padding-bottom: 12px; padding-top: 8px; }
#comments .cmmnt .cmmnt-content header { font-size: 1.3em; display: block; margin-bottom: 8px; }
#comments .cmmnt .cmmnt-content header .pubdate { color: #777; }
#comments .cmmnt .cmmnt-content header .userlink { font-weight: bold; } 
#comments .cmmnt .replies { margin-bottom: 7px; }

In this latter part of the CSS code, all we are doing is positioning content to have extra padding and updating the color schemes.

Final thoughts

The realm of frontend web development is huge and encompasses a large variety of user interfaces. In this tutorial we have looked over some options which allow users more direct contact with each other. This is probably the easiest solution for presenting comments because of how layered conversations appear on a monitor.

Have you built a comment system for a site? What challenges are involved in designing for conversations? Let us know in the comments.

Featured image/thumbnail, comments image via Shutterstock.

Jake Rocheleau

Jake Rocheleau

Jake is a writer and user experience designer on the web. He publishes articles discussing HTML5/CSS3 and jQuery coding techniques. Find out more on his website or you can follow his updates on Twitter @jakerocheleau

Join to our thriving community of like-minded creatives!