CSS Tags With Outlined Borders

The Problem

I’ve just updated my online web development CV and made use of a tag-based system to display the various roles I took on for each of the projects. I was after a very specific look, whereby tags are both shaded (filled) and outlined.

Screenshot of my online web development CV, demonstrating the use of CSS tags.
Screenshot of my online web development CV, demonstrating the use of CSS tags.

A quick scan online didn’t produce any immediate solutions, so I decided to roll my own.

The Solution

The usual method of drawing complex shapes using CSS relies on pseudo elements. There’s a great post on CSS Tricks that details the many shapes that can be created, so I won’t go into detail here. While CSS3 has given us the power to sculpt pixels in ways that weren’t previously possible, we still typically end up with shapes that are ‘filled’ only – meaning that to add a border around a shape can create added complexities. My tag shape was no exception.

See the Pen xGpqzg by Gareth James (@freakyleaf) on CodePen.

The Code

The HTML:

<ul class="tag clearfix">
	<li class="tag-item">Adjule</li>
	<li class="tag-item">Agogwe</li>
	<li class="tag-item">Ahool</li>
	<li class="tag-item">Akkorokamui</li>
	<li class="tag-item">Almas</li>
	<li class="tag-item">Altamaha-Hae</li>
	<li class="tag-item">>Ameranthropoides Loysi</li>
	<li class="tag-item">Amomongo</li>
	<li class="tag-item">Andean Wolf</li>
	<li class="tag-item">Appalachian Black Panther</li>
</ul>

The CSS (LESS):

// VARIABLES

@tag-constant: 25px;
@fill-colour: #FAFAFA;
@border-colour: #CCC;

// GLOBAL STUFF

*, *:before, *:after {
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
}
.clearfix {
	&::before, &::after {
		content: "";
		display: table;
	}
	&::after {
		clear: both;
	}
}

// TAG SPECIFIC STUFF

.tag, .tag-item {
	margin-bottom: 10px;
}
.tag-item, .tag-item::before {
	background: @fill-colour;
}
.tag-item {
	float: left;
	position: relative;
	white-space: nowrap;
	line-height: @tag-constant;
	height: @tag-constant;
	margin-left: (@tag-constant / 2);
	margin-right: 10px;
	padding-left: (@tag-constant / 2);
	padding-right: (@tag-constant / 3);
	border-top-right-radius: .25rem;
	border-bottom-right-radius: .25rem;
	box-shadow: inset 0 0 1px 0 darken(@border-colour, 50%);
	&::before {
		content: "";
		position: absolute;
		left: 0;
		width: (@tag-constant / 100 * 70.71067811865472);
		height: (@tag-constant / 100 * 70.71067811865472);
		transform: rotate(45deg) ;
		transform-origin: top left;
		border: 1px solid @border-colour;
		border-top: none;
		border-right: none;
	}
	&::after {
		content: "";
		position: absolute;
		left: 0;
		top: 50%;
		margin-top: -2.5px;
		width: 5px;
		height: 5px;
		background: #FFF;
		border: 1px solid @border-colour;
		border-radius: 50%;
	}
}

The Explanation

I worked around the problem by using a combination of the good old-fashioned border property the and slightly unconventional box-shadow property to create the outlines I needed. The basic shape isn’t anything unusual, and uses the same thought process as when creating a basic ‘filled’ shape. The element containing the text is given an explicit height, and a pseudo element rotated 45 degrees is shimmied into position to serve as the angled section. The pseudo element is given a border on two sides to give the left-hand side of the shape an outline, and the main element is give a ‘fake’ border using an inset box shadow (note that the colour given to the box shadow is 50% darker than the pseudo element’s border colour, to account for the calculations performed on box shadows by the browser. It’s this mixture of borders and box shadows that allows all lines to join up nicely.

Why Use A Pre-Processor?

I could have used vanilla CSS, but chose to use LESS in this instance for a couple of reasons. Firstly, using a bass variable (@tag-constant) to calculate the tag’s dimensions makes it instantly scalable. Want a bigger tag? Simply change the @tag-constant value and everything will play nicely (try editing the CodePen example above). Secondly, the entire pattern could be turned into a mixin (with @tag-constant set as an argument), which would also make it reusable throughout a project. You could have multiple-sized tags displayed with only one block of code to maintain, which is my preference over constant code repetition any day.