Advanced buttons with icons using CSS Flexbox and modifiers
April 30, 2021
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
}