Creating True Dotted Borders with CSS

Using SVG’s and border-image for flexible, easy-to-maintain border styling.

This is a bit of a long read, feel free to just…


A long-standing client recently approached me for the design and development of their new website. I had done mostly print work for them and proceeded to ask my standard series of questions regarding content. Among them :

What type of media did they want to use and how would they ensure a steady flow of new, quality images?

They were quite clear in their answer to this question: as a small department in a large academic institution, they had no real media needs to speak of, and they wanted to avoid the added task of hunting down and managing images. They wanted the site to focus on their research.

Design Challenge

Adding visual interest to a copy-heavy site without resorting to media.

After carefully choosing and styling the basic typography for the site, I looked back to the print work I had done for them over the past few years and found that I had used large dotted paths in many of their annual reports. We concluded that incorporating this as a graphic element into the project would be an ideal way to create continuity between their print collateral and their upcoming website.

The bonus for me? A slam dunk using border-style: dotted, right? Wrong.

CSS is not always awesome

It turns out that using the border-style: dotted works as long as your border is no larger than a pixel or two. Beyond that there’s no escaping the fact that css dotted borders are nothing more than ugly square pixels. The bigger the border, the bigger the square.

Styling borders with border-style: dotted;

When I jumped into Google looking for a solution I assumed I would find a simple fix within a few minutes. Wrong again. Here are a few clever approaches I came across and what I see as their biggest flaws:

Fix #1

Using an hr tag with an :after pseudo selector (tl;dr = don’t do this!)

If you want to tweak the color, size, or space between dots you can use typographic declarations:

hr {
border: none /* strip default hr styling */
hr:after {
letter-spacing: 10px; /* increase space between dots */
font-size: 6px; /* increase size of dots */
color: palegoldenrod /* pick a color */

The Problems
This is definitely a clever work-around however I found three reasons to avoid this technique:

  • Maintaining this, even via mixins, would be a nightmare.
  • Randomly throwing a million periods between those quotes to cover all lengths feels pretty dirty.
  • What if I need a top and bottom border, or worse, borders on 4 sides to create a box?! I could apply different classes to the various hr’s and use CSS3 rotations to handle vertical borders, but that would be taking “dirty” to a whole new low.

Fix #2

Using CSS border-image property with a PNG

Next I started looking into the border-image property. Essentially you can apply an image to your borders much in the way you apply background images to elements with one major difference: your image will be presented in 9 slices (which you define with the border-image-slice property).

Important to note: the central slice (empty middle square here) represents the areas in which your content will appear.

Setting guides in your graphics editor in order to define the values for the border-image-slice property — in this case 33%.

Example code:

div {
border-width: 0 0 20px 0; /* border-bottom only */
border-image-source: url('path-to-your-png');
border-image-slice: 33%; /* no unit for px, you can use % */
border-image-repeat: round; /* the middle slices will be repeated cleanly up to the edges */
/* Shorthand version */
div {
border-width: 0 0 20px 0;
border-image: url('path-to-your-png') 33% round;

This image illustrates how it actually renders (note the central slice is replaced with your content):

How the border-image-slice property works

I found the border-image spec pretty beefy so I will point you to the always clearer CSS Tricks article for a deeper dive into the required properties and values.

The problem
In order to maintain image quality, the border-width here is determined by the actual size of my dots in the PNG, which brings me to my final point…

PNG is too limiting. SVG to the rescue!

Coming back to my design, I needed a few different dot sizes for various borders (larger for titles, perhaps smaller ones for alert boxes and so on). This would mean creating and maintaining multiple png files. No way!

So I decided to follow the exact same process as fix #2, but saving out my image as an svg rather than a png. By using a vector-based graphic, I could set my dots ( defined by border-width) to any size I wanted!

Using a single SVG, means many different border-widths with no loss of quality!

In the above example I have used 12px border-width for the title, and 7px for the box.


Final Thoughts

SVG’s are still images!
Sadly there is one aspect which prevents this from being as flexible as I would like. This is still an image-based solution and as far as I can tell there is now way to edit an svg which is being called via CSS (i.e. not embedded). If for some reason you wanted to change the colour of your dotted-borders you would still need to edit the SVG file and export it again. If you know of a cool work-around, please comment here!

No support for border-image for IE 10 and below
As of this writing, the border-image property is well supported in all browsers while IE 10 and below remains the problem child. In this case you can declare the regular border-style: dotted as a fallback. It will be ugly, but the layout won’t break.

Safari SVG scaling weirdness
I noticed that Safari will not easily scale the SVG’s. I have read a few different things about scaling for embedded SVG’s but could not find anything with regards to border-images. Any ideas?


I got a few messages that the borders from the codepen were no longer displaying in Chrome and Safari. I did a little digging and apparently these browsers need explicit `border-style` and `border-color` properties. Code has been updated with the fix.

Like what you read? Give Lucas Lemonnier a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.