Semantic Widget Pattern

This is a pattern I’ve been using a lot recently for marking up and styling generic widgets. This has gone through several iterations, the goal each time being semantic markup, as free of presentation-only tags as possible, a full-width header and a padded content area. The goal here is to achieve something that looks like this:

Take 1

My initial pass had been fairly straightforward: a sectioning element (aside, in my case) with a top-level header. Following the header was a div element to handle the padding around the content. Something like this:

Take 1: Markup
1
2
3
4
5
6
7
8
9
10
11
<aside class="widget">

  <h1>Widget Title</h1>

  <div class="widget-content">

    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

  </div>

</aside>
Take 1: Stylesheet
1
2
3
4
5
6
7
8
9
.widget h1 {
  background: #eee;
  width: 100%;
  padding: 10px 0;
}

.widget .widget-content {
  padding: 10px;
}

This is pretty simple and to the point, but I like to eliminate excessive divs wherever I can, so after a while I revisited the problem.

Take 2

My first attempt at eliminating the extraneous div element was to just globally apply padding to every element inside a .widget with the exception of the top-level header.

Take 2: Markup
1
2
3
4
5
6
7
8
9
<aside class="widget">

  <h1>Widget Title</h1>

  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

</aside>
Take 2: Stylesheet
1
2
3
4
5
6
7
8
9
10
11
.widget {
  padding: 10px 0;
}

.widget > * {
  padding: 0 10px;
}

.widget > h1 {
  padding: 10px 0;
}

This seemed to work alright, but there’s still some part of me that feels dirty using global selectors like that.

Take 3

The last revision is the pattern I’ve been using. I was frustrated that it took me so long to see the simplicity of it in the first place, but now it seems like a no-brainer. As I commonly do, I’d overlooked the usefulness of negative margins. Using negative left and right margins on the top-level header we’re able to apply the padding to the .widget itself and not deal with global selectors.

Take 3: Markup
1
2
3
4
5
6
7
8
9
<aside class="widget">

  <h1>Widget Title</h1>

  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>

  <img src="http://placehold.it/400x400" alt="" />

</aside>
Take 3: Stylesheet
1
2
3
4
5
6
7
8
.widget {
  padding: 0 10px;
}

.widget h1 {
  margin: 0 -10px;
  padding: 15px 10px;
}

Demo

View Demo

Thoughts and suggested improvements welcome in the comments.

Comments