No Time Dad

A blog about web development written by a busy dad

Creating Buttons with Icons

Intro

Buttons have really been a pain point for me in the past. Why is it that I can never remember that I need to add margin instead of padding to change the space around a button? I know exactly why I have to do it this way, but somehow the thought always escapes my brain.

I’ve been working on improving my buttons lately. Starting with the baseline button. My jouney to improve my buttons takes me to the icon button today. Which is a button that contains text with an icon either before or after it.

I should mention that I’m not convinced that adding the icon improves the user experience. I understand the context of a button on a page faster when I read the text contained in the button. Not to say that the icon doesn’t help, but my brain naturally wants to read things and process it quickly. Somehow I feel that interpreting an icon takes more time.

I should also mention that I’m notorious for picking bad icons.

Arrow pointing right? Yes, everyone will definitely understand that indicates “send email”

Aside from my mental battle with the user experience of icons in buttons, there’s also the implementation battle. I’ve somehow made it more difficult than it probably needs to be to vertically align the icon with the text. I think I’ve graduated to the next level of alignment mastery by spending more time with flexbox, though.

Buttons with icons are the trend these days, so that’s what I’ll build. But first it would be good for me to shake off my alignment demons with the button icon. I want to be a button making blackbelt.

Text and icon alignment

The important thing to understand is that if I just drop an icon straight into a button element, it’s almost certainly not going to line up with the button text.

<button>
  Button
  <svg class="Icon">...</svg>
</button>
.Icon {
  height: 24px;
  width: 24px;
}

As a white belt in button making, I’d be tempted to add margin-top to the Icon selector in an attempt to push the icon down to better align with the text. This would be a mistake. While it would push the icon down, I’ll have a hard time getting it vertically centered perfectly with the text. I’d end up wasting time trying to get the icon centered. I’ve wasted a lot of time on these kinds of things in the past.

It turns out that flexbox is a great way to save time. Instead of adding margin to the icon I can use flexbox to perfectly center the icon vertically with the text. Making the button element itself a flexbox container is the easiest way for me to accomplish this.

.Icon__Button {
  display: flex;
  align-items: center;
}

.Icon {
  height: 24px;
  width: 24px;
}
<button class="Icon__Button">
  Button
  <svg class="Icon">...</svg>
</button>

It almost brings a tear to my eye seeing how perfectly vertically centered that icon is with the text. Something important to call out here is that I applied a new class called Icon__Button instead of creating a selector for the button element itself. The reason I did that is because I don’t know what other buttons could be on a given page and I might not want them to have this style applied. This is mostly personal preference, but I’ve been bitten by styling elements directly in the past, so I tend to create separate selectors for them to avoid debugging issues later.

The only additional change I’d consider at this point to the button is adding space between the text and the icon. They’re a little close together.

There are probably a few different ways to add the space, the quickest being adding margin-left to the Icon selector. This would be a classic white belt move, though. The problem is that I don’t really know what else is using that Icon selector. The odds are that it’s being reused somewhere else on the page that I might not want want margin-left applied, like a sidenav or menu. This is an embarassing mistake that I’ve made more than a few times.

A better approach would be to use the adjacent sibling combinator instead. That way I only target elements that have Icon__Button and Icon elements next to each other (siblings), and I can feel more confident that the margin is only applying to button icon elements.

.Icon__Button {
  display: flex;
  align-items: center;
}

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

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

Conclusion and full code

I don’t think I’ve made it to button icon black belt status, but I definitely graduated from white belt. Some of my button fears have been aleviated and I ended up with a nice looking button that I can build on.

Full code

Full code and final demo are shown below. The svg icon is from heroicons.

.Icon__Button {
  display: flex;
  align-items: center;
}

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

.Icon__Button + .Icon {
  margin-left: 1rem;
}
<button class="Icon__Button">
  Button
  <svg
    xmlns="http://www.w3.org/2000/svg"
    class="Icon"
    fill="none"
    viewBox="0 0 24 24"
    stroke="currentColor"
  >
    <path
      stroke-linecap="round"
      stroke-linejoin="round"
      stroke-width="2"
      d="M13 7l5 5m0 0l-5 5m5-5H6"
    />
  </svg>
</button>