Drawing Decorative Lines with CSS

In this short article we’ll explore drawing decorative lines with CSS using Pseudo Elements, Background Images, and SVG. After reading you’ll have learned a couple of interesting techniques for drawing section separators or other decorative line elements with CSS.

Pseudo elements

We can generate a pseudo-element using the ::before or ::after selectors. This adds a new child to our targeted element that we can then style as if it was a normal element.

To get the browser to render the pseudo-element we need to set the content property.

.my-element::before {
  content: "I'm a pseudo-element!";
  background: pink;
  padding: 0.25rem;
}

The result:

Now to create a decorative line we position our pseudo-element inside the element we want to draw a line in.

In the demos below I’ve centered the text and given the element a height so we can clearly see the horizontal line running behind the text, note that these properties are not shown in the example CSS.

.my-element {
  position: relative;
  z-index: 0;
}

.my-element::before {
  /* we want to draw a line, so no content needed */
  content: "";

  /* this sets the position and size of the pseudo-element */
  position: absolute;
  top: 50%;
  width: 100%;
  height: 2px;
  background-color: pink;

  /* draw behind child elements */
  z-index: -1;
}

The result:

The .my-element node

This method gives us a lot of flexibility but uses absolute positioning which requires the parent element to have relative positioning. This is because an element with position absolute is positioned relative to the first parent element found that has positioning explicitely set to relative.

This works pretty well but can cause issues when other child elements have z-indexes applied or want to be positioned relative to an element further up in the DOM tree.

We can avoid the positioning issue by using a border like this.

.my-element {
  box-shadow: inset 0 0 0 1px #777;
  color: #777;
}

.my-element::before {
  content: "";
  display: block;
  padding-top: 2.5rem;
  margin-bottom: -2.5rem;
  border-bottom: 2px solid pink;
}

The result:

The .my-element node

Still it feels a bit like a hack to create an element to draw a line and now we need to know the height of the parent element.

Background images

We can use linear-gradient, radial-gradient, and conic-gradient to “generate” background images. Combined with background-size, background-position, and background-repeat we can use these gradients to “cheat” and draw a simple line.

.my-element {
  background-image: linear-gradient(pink, pink);
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: 0 center;
}

The result:

The .my-element node

This is a lot cleaner already, we can make this even better by creating a separate decorative-line class and defining a couple CSS properties.

<p class="my-element decorative-line">Hello!</p>
.decorative-line {
  background-image: linear-gradient(var(--line-color), var(--line-color));
  background-repeat: no-repeat;
  background-size: 100% var(--line-height);
  background-position: 0 center;
}

.my-element {
  --line-color: pink;
  --line-height: 2px;
}

Now we can apply the decorative-line class to an element and set the --line-color and --line-height properties to style it.

We can now also fade-in and fade-out lines.

.my-element {
  background-image: linear-gradient(to right, pink, white 35%, white 65%, pink);
  background-repeat: no-repeat;
  background-size: 100% 2px;
  background-position: 0 center;
}

The result:

The .my-element node

We can still set a background color and have multiple other background images on the target element.

.my-element {
  background-color: lavenderblush;
  background-image: linear-gradient(pink, pink), linear-gradient(palevioletred, palevioletred);
  background-size: 100% 2px, 2px 100%;
  background-position: 0 center, 25% 0;
  background-repeat: no-repeat;
}

The result:

The .my-element node

SVG data URIs

We can also set background images using SVGs but unfortunately we cannot use CSS variables inside the SVG data URI.

.my-element {
  background-image: url('data:image/svg+xml,\
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100% 100%">\
      <rect x="0" y="50%" width="100%" height="2px" fill="pink"/>\
    </svg>');
}

The result:

The .my-element node

I haven’t tested this but I suspect using SVGs is slower than linear-gradient as it requires the browser to decode and draw the SVG. nonetheless SVG is very powerful so might be the way to go if you need to draw very exotic lines.

Conclusion

There’s multiple ways we can draw decorative lines with CSS. We found pseudo elements give us a lot of flexibility but also require positioning that could introduce side effects.

Using generated background images instead can be a nice work around and doesn’t introduce the positioning problems that pseudo-elements can cause.

SVGs are also a nice alternative but can’t be styled with CSS Custom Properties and might be a bit slower than linear gradients.

I’d love to find out about other solutios to this problem so do hit me up on Twitter if you have any comments.

Rik Schennink

Web enthusiast

to pqina.nl