Heading Set Styling with CSS

In this tutorial we will create a couple of heading styles to get your creative juices flowing for your next project. Make heading styles work by applying the right combinations of colors, fonts and decoration.

In this tutorial I’m hoping to give you some ideas and inspiration for your current and future projects!

Today, we are going to talk about headings and titles. When you put some content on the web (or on a paper), you have to set up some kind of structure. And this is where you need headings: to give sense to your content.

We all know how difficult it is to find the appropriate title for the right content, and frankly I’m not good at it so this won’t be the point of the article. Instead, we are going to talk about design.

How to design a good title, or a good arborescence of titles? I didn’t think much about it until then so I thought it could be a nice exercise, so here we are. I tried to create a few sets of headings, each one with its own feeling.

A few notes before we start:

  • Every font used in this tutorial is a free font from Google WebFonts.
  • You won’t see any vendor prefixes in the CSS snippets, but you will, of course, find them in the files.
  • I use the box-model where [width] = [element-width] + [padding] + [borders]. I activate it with the following snippet:
*, *:after, *:before {
	box-sizing: border-box;
}

I don’t think there is much need for an explanation about the markup used in the demos: I used 4 titles, from h1 to h4.

Why not h5 and h6 you say? Simply because it’s not very usual to have such a depth for normal websites. But if you have one which requires those levels of headings, please be sure to include some styles for them as well. 😉

A few words on vertical rhythm

What is vertical rhythm?

Vertical rhythm is a very important thing when it comes to web design. It is the concept implying consistent spacing between elements on a page, especially typographic elements.

When you write a blog post which is divided into multiple sections like this one, you may want to have proportional spacing around each element, such as titles, paragraphs, images, lists, etc.

Let me quote Richard Rutter from his great 24 Ways article on vertical rhythm:

On the Web, vertical rhythm – the spacing and arrangement of text as the reader descends the page – is contributed to by three factors: font size, line height and margin or padding. All of these factors must calculated with care in order that the rhythm is maintained.

Since I’m not a vertical rhythm expert, I decided to not reinvent the wheel and go with a few lines of CSS as a default base to write my demos.

Vertical rhythm in CSS

h1 {
    font-size: 36px;
    line-height: 40px;
}

h2 {
    font-size: 30px;
    line-height: 40px;
}

h3 {
    font-size: 24px;
    line-height: 40px;
}

h4 {
    font-size: 18px;
    line-height: 20px;
}

/* Won't be used here */

h5 {
    font-size: 14px;
    line-height: 20px;
}

h6 {
    font-size: 12px;
    line-height: 20px;
}

I also gave a few styles to paragraphs to make them fit to the headings. But we are going to focus on the headings only here.

Now that we have covered the basics of our exercise, we can create a few sets of headings!

Note that each heading will have its styles defined individually, so you will see a lot of repetition of styles among the headings. When you write your styles, you should of course summarize them to avoid repetition.

Example 1

Headings01

Let’s start with something pretty simple. Not much involved here, just a few font styles.

Fonts used: Ultra (sans-serif), Orienta (sans-serif).

.demo-1 .main h1 {
	margin: 1em 0 0.5em 0;
	color: #343434;
	font-weight: normal;
	font-family: 'Ultra', sans-serif;   
	font-size: 36px;
	line-height: 42px;
	text-transform: uppercase;
	text-shadow: 0 2px white, 0 3px #777;
}

.demo-1 .main h2 {
	margin: 1em 0 0.5em 0;
	color: #343434;
	font-weight: normal;
	font-size: 30px;
	line-height: 40px;
	font-family: 'Orienta', sans-serif;
}

.demo-1 .main h3 {
	margin: 1em 0 0.5em 0;
	color: #343434;
	font-size: 22px;
	line-height: 40px;
	font-weight: normal;
	text-transform: uppercase;
	font-family: 'Orienta', sans-serif;
	letter-spacing: 1px;
	font-style: italic;
}

.demo-1 .main h4 {
	margin: 1em 0 0.5em 0;
	color: #343434;
	font-size: 18px;
	line-height: 20px;
	font-weight: normal;
	font-family: 'Orienta', sans-serif;
}

Okay, it was pretty straightforward. Let’s try something a little more detailed.

Example 2

Heading02

This examples shows how to add something to the background of your heading, be it solid color or even an image. The large inset white box shadow for the h1 will ensure that the image does not “collide” with the headline text when we view it on a small screen. The left padding has a percentage value for the same reason: when the viewport becomes very small, we want to keep the padding relative to the width, i.e. keep it fluid.

Fonts used: Titillium Web (sans-serif), Muli (sans-serif).

The CSS

.demo-2 .main h1 {
	margin: 1em 0 0.5em 0;
	font-weight: 600;
	font-family: 'Titillium Web', sans-serif;
	position: relative;  
	font-size: 36px;
	line-height: 40px;
	padding: 15px 15px 15px 15%;
	color: #355681;
	box-shadow: 
		inset 0 0 0 1px rgba(53,86,129, 0.4), 
		inset 0 0 5px rgba(53,86,129, 0.5),
		inset -285px 0 35px white;
	border-radius: 0 10px 0 10px;
	background: #fff url(../images/bartoszkosowski.jpg) no-repeat center left;
}

.demo-2 .main h2 {
	margin: 1em 0 0.5em 0;
	font-weight: normal;
	position: relative;
	text-shadow: 0 -1px rgba(0,0,0,0.6);
	font-size: 28px;
	line-height: 40px;
	background: #355681;
	background: rgba(53,86,129, 0.8);
	border: 1px solid #fff;
	padding: 5px 15px;
	color: white;
	border-radius: 0 10px 0 10px;
	box-shadow: inset 0 0 5px rgba(53,86,129, 0.5);
	font-family: 'Muli', sans-serif;
}

.demo-2 .main h3 {
	margin: 1em 0 0.5em 0;
	font-weight: 600;
	font-family: 'Titillium Web', sans-serif;
	position: relative;
	text-shadow: 0 -1px 1px rgba(0,0,0,0.4);
	font-size: 22px;
	line-height: 40px;
	color: #355681;
	text-transform: uppercase;
	border-bottom: 1px solid rgba(53,86,129, 0.3);
}

.demo-2 .main h4 {
	margin: 1em 0 0.5em 0;
	font-weight: 600;
	font-family: 'Titillium Web', sans-serif;
	position: relative;
	font-size: 18px;
	line-height: 20px;
	color: #788699;
	font-family: 'Muli', sans-serif;
}

Example 3

Headings03

I thought it could be a good idea to have a darkish demo. Many sites are using a pretty dark template, like Compass or CodePen.

Once again, I picked an electric blue to go with the dark gray background, but you could do with whatever pleases you. Anyway, I really suggest a punchy color.

Fonts used: Hammersmith One (sans-serif), Questrial (sans-serif).

The CSS

  
.demo-3 .main h2:after, 
.demo-3 .main h3:after, 
.demo-3 .main h4:after {
	position: absolute;
	content: "";
	left: 0;
	top: 0;
	bottom: 0;
	width: 5px;
	border-radius: 2px;
	box-shadow: 
		inset 0 1px 1px rgba(0,0,0,0.5), 
		0 1px 1px rgba(255,255,255,0.3);
}

.demo-3 .main h2:after { background: #0AF; }
.demo-3 .main h3:after { background: #3BF; }
.demo-3 .main h4:after { background: #6Cf; }

.demo-3 .main h1 {
	font-size: 36px;
	line-height: 40px;
	margin: 1em 0 .6em 0;
	font-weight: normal;
	color: white;
	font-family: 'Hammersmith One', sans-serif;
	text-shadow: 0 -1px 0 rgba(0,0,0,0.4);
	position: relative;
	color: #6Cf;
}

.demo-3 .main h2 {
	margin: 1em 0 .6em 0;
	padding: 0 0 0 20px;
	font-weight: normal;
	color: white;
	font-family: 'Hammersmith One', sans-serif;
	text-shadow: 0 -1px 0 rgba(0,0,0,0.4);
	position: relative;
	font-size: 30px;
	line-height: 40px;
}

.demo-3 .main h3 {
	margin: 1em 0 .6em 0;
	padding: 0 0 0 20px;
	font-weight: normal;
	color: white;
	font-family: 'Hammersmith One', sans-serif;
	text-shadow: 0 -1px 0 rgba(0,0,0,0.4);
	position: relative;
	font-size: 24px;
	line-height: 40px;
	font-family: 'Questrial', sans-serif;
}

.demo-3 .main h4 { 
	margin: 1em 0 .6em 0;
	padding: 0 0 0 20px;
	font-weight: normal;
	color: white;
	font-family: 'Hammersmith One', sans-serif;
	text-shadow: 0 -1px 0 rgba(0,0,0,0.4);
	position: relative;
	font-size: 18px;
	line-height: 20px;
	font-family: 'Questrial', sans-serif;
}

Why not a border?

Very good question, my lord! At first, I did use a left border for this demo. It worked like a charm, was fully compatible back to IE1 or something (!).

Then I wanted to add a subtle light effect to this border. And maybe rounded corners. How am I going to do that? With a pseudo-element of course! Design purpose, no extra markup, perfect use case!

What about old browsers you say? Yup, IE6 and IE7 won’t see your magnificent border. From there, you have 2 options:

  • Give those browsers a left border as a fallback
  • Don’t give a damn since it’s a purely visual issue.

I think I’d give the border. 😉

Example 4

Headings04

I heard you like wood. Right?

Note: the little leaf on the second level of the title is from FontAwesome. The icon font was “constructed” using Fontello.

Fonts used: Scada (sans-serif), Carrois Gothic (sans-serif).

The CSS

.demo-4 .main h1 i, 
.demo-4 .main h2 i, 
.demo-4 .main h3 i, 
.demo-4 .main h4 i {
	padding-right: 10px;
	color: #A8D13B;
	font-size: 0.8em;
}

.demo-4 .main h2:after, 
.demo-4 .main h3:after, 
.demo-4 .main h4:after {
	position: absolute;
	content: "";
	height: 1px;
	border-radius: 2px;
	left: 0;
	bottom: 0;
	box-shadow: 
		0 -1px 0 rgba(0,0,0,0.1), 
		0 1px 0 rgba(255,255,255,0.6);
}

.demo-4 .main h2:after { width: 100%; } 
.demo-4 .main h3:after { width:  75%; }
.demo-4 .main h4:after { width:  50%; }

.demo-4 .main h1 {
	margin: 1em 0 0.75em;
	padding: 0 0 5px 0;
	color: #6B5344;
	font-weight: normal;
	position: relative;
	text-shadow: 0 2px 0 rgba(255,255,255,0.5);
	font-size: 36px;
	line-height: 40px;
	font-family: 'Carrois Gothic', sans-serif;
}

.demo-4 .main h2 {
	margin: 1em 0 0.75em;
	padding: 0 0 5px 0;
	color: #6B5344;
	font-weight: normal;
	font-family: 'Scada', sans-serif;
	position: relative;
	text-shadow: 0 2px 0 rgba(255,255,255,0.5);
	font-size: 30px;
	line-height: 40px;
}

.demo-4 .main h3 {
	margin: 1em 0 0.75em;
	padding: 0 0 5px 0;
	color: #6B5344;
	font-weight: normal;
	font-family: 'Scada', sans-serif;
	position: relative;
	text-shadow: 0 2px 0 rgba(255,255,255,0.5);
	font-size: 24px;
	line-height: 40px;
}

.demo-4 .main h4 {
	margin: 1em 0 0.75em;
	padding: 0 0 5px 0;
	color: #6B5344;
	font-weight: normal;
	font-family: 'Scada', sans-serif;
	position: relative;
	text-shadow: 0 2px 0 rgba(255,255,255,0.5);
	font-size: 18px;
	line-height: 20px;
}

Why not a border, again?

It’s pretty much the same reason as for the previous demo. I didn’t use a bottom border because we can’t set a width to it and I wanted the underline to be smaller as you go deeper in title levels.

One solution would have been to set a width to the titles in order to make the bottom-border behave accordingly, but it’s pretty dirty and could be problematical in long titles.

So pseudo-element. Plus, the browser support for pseudo-elements is pretty good nowadays.

Example 5

Headings05

I think this example is a very good one because it makes the perfect use case for CSS counters. Yes, counters with CSS. I’m sure a few of you don’t even know we could do this kind of magic tricks with CSS!

Fonts used: Orienta (sans-serif).

A word on CSS counters

To put it simple, CSS (2.1!) provides us a way to assign a counter to a type of element, incrementing or decrementing it at each occurrence. Then, with pseudo-elements we can display this counter accordingly.

There are 2 properties regarding counters (counter-reset and counter-increment) and 2 values for the content property (counter() and counters()).

Basically, you reset the counter on the wrapper, and then you increment it on the children. And since a good demo is always better than a long read, please have a look below.

The CSS


.demo-5 .main {
	counter-reset: section-1, section-2, section-3, section-4;
}

.demo-5 .main h1 {
	margin: 0.8em 0 0.5em 0;
	color: #333;
	font-weight: normal;
	font-family: 'Orienta', sans-serif;
	font-size: 36px;
	line-height: 40px;
	counter-increment: section-1;
	counter-reset: section-2 section-3 section-4;
}

.demo-5 .main h2 {
	margin: 0.8em 0 0.5em 0;
	color: #333;
	font-weight: normal;
	font-family: 'Orienta', sans-serif;
	font-size: 30px;
	line-height: 40px;
	counter-increment: section-2;
	counter-reset: section-3 section-4;
	border-bottom: 1px solid #fff;
	box-shadow: 0 1px 0 rgba(0,0,0,0.1);
	padding-bottom: 10px;
}

.demo-5 .main h3 {
	margin: 0.8em 0 0.5em 0;
	color: #333;
	font-weight: normal;
	font-family: 'Orienta', sans-serif;
	font-size: 24px;
	line-height: 40px;
	counter-increment: section-3;
	counter-reset: section-4;
}

.demo-5 .main h4 {
	margin: 0.8em 0 0.5em 0;
	color: #333;
	font-weight: normal;
	font-family: 'Orienta', sans-serif;
	font-size: 18px;
	line-height: 20px;
	counter-increment: section-4;
}

.demo-5 .main h1:before { content: counter(section-1) ". "; }
.demo-5 .main h2:before { content: counter(section-1) "." counter(section-2) " "; }
.demo-5 .main h3:before { content: counter(section-1) "." counter(section-2) "." counter(section-3) " "; }
.demo-5 .main h4:before { content: counter(section-1) "." counter(section-2) "." counter(section-3) "." counter(section-4) " "; }

To sum up: each level of title increments its own counter and displays (thanks to the :before pseudo-element) it and all the counters of upper levels of headings.

For further readings about CSS counters, please refer to:

Example 6

Headings06

This is a light example with some color. Headings don’t always need to have a heavy font weight; we can also create impact with a very light font if we make it large enough and captivating using color and text shadows.

This example also show how uppercase and italics can be used.

Fonts used: Josefin Sans (sans-serif).

The CSS

.demo-6 .main h1 {
	margin: 1em 0 0.5em 0;
	font-weight: 100;
	text-transform: uppercase;
	color: #00caa6;
	font-style: italic;
	font-family: 'Josefin Sans', sans-serif;
	font-size: 58px;
	line-height: 54px;
	text-shadow: 2px 5px 0 rgba(0,0,0,0.2);
}

.demo-6 .main h2 {
	margin: 1em 0 0.5em 0;
	color: #148773;
	font-size: 26px;
	line-height: 40px;
	font-weight: bold;
	font-family: 'Josefin Sans', sans-serif;
}

.demo-6 .main h3 {
	margin: 1em 0 0.5em 0;
	color: #343434;
	font-size: 22px;
	line-height: 40px;
	font-weight: 100;
	text-transform: uppercase;
	font-family: 'Josefin Sans', sans-serif;
	letter-spacing: 1px;
	font-style: italic;
}

.demo-6 .main h4 {
	margin: 1em 0 0.5em 0;
	color: #343434;
	font-size: 18px;
	line-height: 20px;
	font-weight: bold;
	font-family: 'Josefin Sans', sans-serif;
}	

What about CSS pre-processors?

It occurred to me that mixins from CSS pre-processors (whichever you are using) can be very useful when it comes to such repetitive CSS. Actually, we could build simple functions accepting parameters to ease our life.

Let me show you what I mean with a LESS version of our first demo.

html {
    font-size: 62.5%;
}

.headings(@font, @size, @lh, @ls, @style, @transform) {
   margin: 1em 0 0.5em 0;

   /* REM calculations */
   @sizeREM: @size/10;
   @lhREM: @lh/10;
   
   /* Fallbacks */
   font-size: ~"@{size}px";
   line-height: ~"@{lh}px";
  
   /* Font styles */
   font-size: ~"@{sizeREM}rem";
   line-height: ~"@{lhREM}rem";
   font-family: @font~", sans-serif";
   font-style: @style;
   color: #343434;
   letter-spacing: ~"@{ls}px";
   text-transform: @transform;
}

h1 {
    .headings(Ultra, 36, 42, 0, normal, uppercase);
    text-shadow: 0 2px white, 0 3px #777;
}

h2 { .headings(Orienta, 30, 40, 0, normal, capitalize); }
h3 { .headings(Orienta, 22, 40, 1, italic, uppercase); }
h4 { .headings(Orienta, 18, 20, 0, normal, capitalize); }

Note: the font-size declaration on the html element is to trigger a base 10 convertion. Default size is 16px. 16 * 62.5 / 100 = 10. From there, convertion to rem is way easier.

I took the time to add the declarations for both rem and px units in the mixin, and with a little more work we could even tweak a bit our function to use the shorthand property for font settings (font).

Here is the syntax for LESS, but you could do something very similar with Sass/SCSS or Stylus. I’m just used to LESS. 🙂

Final words

As a final word, I’d like to stress the fact typography really matters a lot in the web, at least as much as in print. It’s even more true when it comes to blogs, and editorial content.

As further readings, I’d recommend:

Thanks a lot for reading this article and as always, please share any related concepts or comment. 🙂

Tagged with:

Kitty Giraudel

Non-binary accessibility & diversity advocate, frontend developer, author. Real life cat. They/she.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 17

Comments are closed.
  1. Awesome, all different heading style at one place. Lots of options to compare. Thanks a lot ! 🙂

  2. Thank you for this. Nothing frustrates me more than inconsistent styling of headings.

  3. The leaf is not visible on #4 with the latest Opera browser, otherwise thanks for shearing this great examples and reach descriptions.

  4. Hugo, U’r superawesome!

    I can’t stop to wonder about your wit…..so versatile!!!

    Cheers,
    AS

  5. Vertical Rhythm – I like the sound of that. You sir, have provided some great examples of consistency in headings. A messed up blog out there thanks you!

  6. Demo 5 is very impressive. I tested it out myself and seriously like the way it automatically adds the counter. Can see that being very useful if you want to move sections around and have the numbering auto-update.

    It’s worth pointing out that you could achieve something similar by wrapping the headings in ordered list tags, but this method is more powerful and requires less HTML code too.

  7. Very nice! Expecially the part about CSS counter. And I love someone else than me uses LESS, because articles with Sass are usually presented here. Thanks man!

  8. Hugo:
    Thanks for the tutorial. I don’t see how you made the level 3 paragraphs multicolumn. Please point out thanks.

    • Hi! It’s very easy:
      column-count: 3; column-gap: 15px;

      Don’t forget vendor prefixes. 🙂

  9. By the way, Compass SCSS framework has a built-in support for vertical rhythm. Besides that there is a great grid framework over compass called Susy, written by Eric Meyer, which even more extends vertical rhythm support.

  10. Headings need to be location-independent (or predictable) and defined once according to oocss methodology and linting rules.

    I know the author probably did this for the demo, but I don’t want readers to see this and think it’s acceptable practice.