No Time Dad

A blog about web development written by a busy dad

Advanced buttons with icons using CSS Flexbox and modifiers

Using css modifier classes isn’t something I’ve taken enough advantage of. I can create baseline selectors for elements to use across multiple projects and add modifiers to customize the baseline elements for the specific project I’m working on. Really helpful. In fact, it’s especially helpful for buttons and form elements.

In a previous post I built a simple icon button. I’ll apply the styles and structure used there, primarily flexbox, to create a new baseline button. I’ll be building on that example here by adding modifiers to make the button more flexible.

The example starting point code and demo are below.

.Icon__Button {
  display: flex;
  align-items: center;
  background-color: #fff;
  border-color: #dbdbdb;
  border-width: 1px;
  color: #363636;
  border-radius: 0.5rem;
  padding: 0.5rem 1rem;
}

.Icon__Button:hover {
  border-color: #b5b5b5;
  color: #363636;
}

.Icon__Button:focus {
  outline: none;
  border-color: #485fc7;
  color: #363636;
  box-shadow: 0 0 0 0.125em rgba(72, 95, 199, 0.25);
}

.Icon {
  height: 24px;
  width: 24px;
}

.Icon__Button + .Icon {
  margin-left: 0.5rem;
}
<button class="Icon__Button">
  Download
  <svg class="Icon">...</svg>
</button>

Anytime I create a new button and add the Icon__Button and Icon classes to it, it’ll automatically have left margin applied to the icon. Which immediately paints me into a corner. If I wanted the icon on the left before the text, I’d have a new spacing issue.

<button class="Icon__Button">
  <svg class="Icon">...</svg>
  Download
</button>

I’ve got some decisions to make at this point. Do I stick with the Icon__Button selector as it is and create a modifier selector called Icon__Button__Left to handle cases where I want the icon on the left? Or do I rename Icon__Button to just Button and create two new modifier classes Icon__Right and Icon__Left to handle the spacing? Or some other option that I haven’t thought of?

I don’t know if there’s a correct answer. It depends on how far into building the given component or page I already am and what css already exists. Personally, I think the best and most extensible solution is the three selector solution where I use Button with left and right modifiers, Button__Icon__Right and Button__Icon__Left.

In the long run, I think it’ll be easier to do the extra work and just make the extra modifier classes now to save me from some headaches later. So, I’ll rename my initial Icon__Button selector to Button, and I’ll create two new selectors that’ll be modifiers for the Button selector. Below is how the updated code will look, as well as a demo for how the Button__Icon__Left modifier will look.

/* Renamed selector */
.Button {
  background-color: #fff;
  border-color: #dbdbdb;
  border-width: 1px;
  color: #363636;
  border-radius: 0.5rem;
  padding: 0.5rem 1rem;
  display: flex;
  align-items: center;
}

.Button:hover {
  border-color: #b5b5b5;
  color: #363636;
}

.Button:focus {
  outline: none;
  border-color: #485fc7;
  color: #363636;
  box-shadow: 0 0 0 0.125em rgba(72, 95, 199, 0.25);
}

.Icon {
  height: 22px;
  width: 22px;
}

/* New modifier comes after .Button */
.Button__Icon__Left {
  margin-right: 0.5rem;
}

/* New modifier comes after .Button */
.Button__Icon__Right {
  margin-left: 0.5rem
}
<button class="Button">
  <svg class="Icon Button__Icon__Left">...</svg>
  Download
</button>

Now there isn’t any extra space to the left of the icon when it’s on the left. If I wanted an icon on the right instead, I’d just swap out the left modifier for the right and move the icon element after the text.

<button class="Button">
  Download
  <svg class="Icon Button__Icon__Right">...</svg>
</button>

An important thing to remember about modifiers is that the order in which the modifier class appears in the css file is important. CSS stands for Cascading Style Sheets, which means that styles can (and will) overwrite one another as the browser reads the file from the top down. I’m adding new properties in addition to the Icon selector properties in this example, but if I had a modifier that increased the width of the icon and that modifier came before the Icon selector then my size increase would be overwritten.

Conclusion & Example code

Experimenting with modifiers is actually a lot of fun. They have made my css more re-usable and solve design problems in an interesting way. As with things in css, there are probably ten different ways to solve the problem of multiple icon positions. I like the way I choose, though. I think it’s easy to understand what’s happening at a glance and it was quick to implement.

Below is the code used in the demos. The icon is from heroicons.

<button class="Button">
  <svg class="Icon Button__Icon__Left" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
    <path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd" />
  </svg>
  Download
</button>
.Button {
  background-color: #fff;
  border-color: #dbdbdb;
  border-width: 1px;
  color: #363636;
  border-radius: 0.5rem;
  padding: 0.5rem 1rem;
  display: flex;
  align-items: center;
}

.Button:hover {
  border-color: #b5b5b5;
  color: #363636;
}

.Button:focus {
  outline: none;
  border-color: #485fc7;
  color: #363636;
  box-shadow: 0 0 0 0.125em rgba(72, 95, 199, 0.25);
}

.Icon {
  height: 22px;
  width: 22px;
}

.Button__Icon__Left {
  margin-right: 0.5rem;
}

.Button__Icon__Right {
  margin-left: 0.5rem
}