30 November 2009

Creating circles with CSS

Most experienced web designers will know about -moz-border-radius and -webkit-border-radius.  These are the magic bits of CSS that give boxes gorgeous rounded corners.  Of course, it's not standard, and doesn't work on IE or Opera, but for the enlightened of us we can take advantage of those features in Firefox, Safari and Chrome.  But can we take it to the next level?  Can we reach the next nirvana in design and create circles using just CSS?

NOTE: the following examples only work in compatible browsers, i.e. Firefox, Safari, Chrome.  At the moment it will not work in Internet Explorer or Opera.  This has been tested on a Mac, mileage may vary on PCs.

Example 1: a simple circle

The key to creating a circle is understanding what the border radius means.  When Firefox sees -moz-border-radius: 10px; it effectively draws a quarter circle centred 10px in from the corner of the box.  It'll look like this:

Hello world!

Now, to create a circle all we need to do is make sure that each of those corners have their centres at exactly the same point - the very centre of the box.  Those four quarter circles will then match up perfectly and give you a nice CSS circle.  We'll need to set the width and height to the same value, say 100px, and then set the border radius to be half of that, i.e. 50px.

Hello world!

The CSS for that looks like this:

.circle {
    width: 100px; height: 100px;
    background: #ff0000; color: #ffffff;
    -moz-border-radius: 50px; -webkit-border-radius: 50px;
}

Hoorah!  It's a circle!

However, you can see that we still have some work to do to get the text inside to display sensibly.  For this example we're going to assume that the text inside is going to be just a couple of words, and we want them to display right in the centre of the circle.

To get the text centred horizontally all we need to do is use text-align: center;.  For the vertical alignment, since we're assuming it will only be one line we can set the line-height to be the height of the circle.  That gives us the following:

Hello world!

.circle {
    width: 100px; height: 100px;
    background: #ff0000; color: #ffffff;
    -moz-border-radius: 50px; -webkit-border-radius: 50px;
    text-align: center;
    line-height: 100px;
}

There, that's much better.

Example 2: A circle with more text

What happens if we need to fit more than one line of text into that circle?  Well, at the moment the next line will appear below, outside the circle, which isn't quite what we want.  The reason for this is that we have set the line-height to 100px.

To get around this problem we need to abandon the idea of using line-height and rely on padding instead.  This will probably mean that we can't guarantee the text will be centred vertically, so that's worth bearing in mind.  Also, because we are adding padding to the sides of our circle, we will need to adjust the width and height to compensate.  So if we give our circle 20px padding top and bottom, and 10px at the sides, we'll have to have a height of 60px and a width of 80px to maintain the same overall dimensions.

This is a circle with a little more text in it

.circle {
    width: 80px; height: 60px;
    background: #ff0000; color: #ffffff;
    -moz-border-radius: 50%; -webkit-border-radius: 50px;
    text-align: center;
    padding: 20px 10px;
}

You'll also notice that I've changed the border radius to 50%.  That's just another way of making sure that the border radius is kept to the centre irrespective of the actual overall dimensions (which means you can scale the circle more easily).  Unfortunately Safari doesn't like percentages though, so I've left that as pixels; you can use ems though in both cases.

Example 3: larger content with paragraph shaping

That's fine, I hear you say, but what if we have bigger circles with longer content?  Sadly, this is where HTML and CSS alone fall short of the sort of tools traditional designers have available to them.  On the web there is no such thing as internal text-wrap, for a simple reason - our circle is actually a square.  The rounded corners affect the way the box is rendered, not how the contents respond to those corners.  If we have a larger circle with a long paragraph of text, using the above technique, the text will be placed completely ignoring the circle behind it:

This is a circle with a little more text in it. This circle is bigger, and this paragraph of text is longer, but the text inside doesn't respect the rounded corners because CSS isn't that clever. Yet.

I've changed the text colour slightly so you can see it overflowing against the white background.

The simplest, yet also the clumsiest, way to resolve this is to add in line breaks strategically to shape the text into something resembling a circle.  It takes a little trial-and-error, but with a little work you might end up with something like the following:

This is a
circle with a little
more text in it. The circle
is bigger, and we have
line breaks put in to shape
the text to give the
impression of a circle. It's
not perfect though, and
makes the code
very messy.

We've not changed the CSS much (apart from the size of the circle), but here's the HTML:

<div class="circle">This is a <br />circle with a little <br />more text in it. The circle <br />is bigger, and we have <br />line breaks put in to shape <br />the text to give the <br />impression of a circle. It's <br />not perfect though, and <br />makes the code <br />very messy.</div>

It's better but it's not perfect.  And, more importantly, I can only guarantee that it will work on my computer.  When viewed on a different computer, using a different browser, or using a different text style, it might look very different.  And with all those <br /> tags in seemingly arbitrary positions through the text it's not going to be brilliant for screen readers, mobile internet or for anyone not using the CSS file.

Example 4: larger content with shaping floats

This next approach is a little more semantically friendly, though it too is not without its failings.  Instead of trying to shape the paragraph to a circle by tweaking the text from the inside, we place objects in each corner to push the text around from the outside instead.  This does involve some additional HTML markup, and some more CSS.

This is a circle with a little more text in it. The circle is fairly big, and we have put floating elements in the corners to move the text around to
make it look
more circular than square.

The additional CSS:

.circleCorner {
    float: left;
    width: 30px; height: 30px;
    border: 1px solid #c0c0c0;
}

And the HTML:

<div class="circle">
  <div class="circleCorner"></div><div class="circleCorner"></div>This is a circle with a little more text in it. The circle is fairly big, and we have put floating elements in the corners to move the text around to <div class="circleCorner"></div>make it look <div class="circleCorner"></div>more circular than square.
</div>

These additional objects have been given a thin border so you can see the effect they're having, and I've also set the text align to justify to make it even clearer what's going on.  Basically, we are indenting the text in the problem areas, i.e. the corners.  Clearly, this isn't going to be nearly good enough if you have a bigger circle with more content, because we are effectively squashing the content into a cross rather than a circle, but with the dimensions used in this example we may just be able to get away with it by careful choice of words, a little trial-and-error with the element sizes, and a wing and a prayer.

Conclusion

So, can you create text in a circle using just CSS?  Yes.  Within reason.

The above examples only work in compatible browsers, and given the stranglehold IE still has on the world, this shouldn't yet be considered to be a viable option for mainstream web sites that are relying on the circles being visible.  We've also shown that its effectiveness is limited when using anything more than a few words - it certainly isn't a reliable solution if you have large chunks of text to place inside a circle.  At this point in the history of CSS, unfortunately the best solution is still to create an image instead.

RSS Twitter
Share this page: Delicious Digg Facebook StumbleUpon Tweet Email
Matthew Dawkins

This post was originally published on www.matthewdawkins.co.uk.
Copyright © Matthew Dawkins 2009

About Matthew Dawkins

Matthew is a web designer based in Somerset, UK. He has a passion for CSS and design, and runs his own web design business.