Managing icon component sizes

Many design systems want to restrict the number of sizes that icons can be used at. Constraints like this are often helpful, as a closed set of sizes gives consumers confidence their designs are adhering to the system's standards.

Though, not every system has this restriction.

But for the ones that do, I see two popular approaches:

  • A <span class="inline-code">size</span> variant property on each icon component.
  • A <span class="inline-code">size</span> variant property on an <span class="figma-component">❖ icon wrapper</span>, with an instance swap property for selecting icon glyphs.

I considered other possible set ups using variable modes and separate components, but quickly dismissed them. I'll explain why before discussing to the two popular approaches above.

Bad: Using separate components for each icon size

This would look like <span class="figma-component">❖ arrow 12px</span>, <span class="figma-component">❖ arrow 16px</span>, <span class="figma-component">❖ arrow 20px</span>.

In some ways, this set up could make some aspects of managing the icon collection easier for a librarian. Namely, making bulk edits to existing icons.

Updating 24px size icons to 22px

The downside to this approach primarily lies with the consumer experience. Managing icon sizes as separate components makes the asset panel all the more annoying to use.

If you have 100 icons, and offer 3 sizes, that's 300 icons to sift through. Yes, consumers will likely search for the glyph they want by name, but unlike button hierarchies, they'll see 3 visually identical copies of the glyph they're looking for every single time.

Searching the asset panel with keyword "alarm" returns an overwhelming amount of results

I've played around with Figma's native "select all matching..." feature, as well as various community plugins. But they all struggled with selecting similar but discrete variants across multiple variant sets.

Removing variants from the equation means bulk selection and manipulation becomes possible (and just barely—I still had to use find and replace to select the icons I wanted in the earlier GIF).

Because this approach makes librarians work easier at the expense of the consumer experience, I consider it non-viable (much like base components).

Also bad: Using variable modes for icon sizes

Picture a variable collection with modes small, default, and large. Then picture a single number variable with values that match each modes name.

That number variable would be applied to the height and width of each icon component. You could even apply it to each icon's minimum and maximum widths if you really wanted to police icon sizing.

For librarians, this approach has similar advantages as the <span class="figma-component">❖ icon wrapper</span> method. It offers a single place to manage icon sizes for every icon in the library.

But is also lets you do some fancier things...

It's not hard to picture boolean variables that show and hide certain parts of the artwork at different sizes. Those small, default, and large modes could be more than just changing dimensions... they can also change the amount of detail displayed.

houses scale
"Shop" icon at 3 different sizes, from Bonnie Kate Wolf's A Complete Guide to Iconography

What's more is you can keep those strokes live and unflattened. If you're worried about preserving color overrides, fear not. I have another blog post addressing that.

Consider the SVG layer for the doorknob. To make sure it doesn't show up on smaller modes, it would need a boolean variable applied to it's visibility that is only "true" in the large size mode.

Boolean variables to have pieces of icons shown and hidden across sizemodes

Notice I didn't name the boolean variable "doorknob". That would be too specific to be useful. Instead, I named the variable according to the situation I want to use it in: "only show this element in this (or these) mode(s)." That way I could reuse it across other icon glyphs too.

On the consumer end, this could seem like an odd way to use variable modes. Typically modes are applied to whole-mockups for theming, rather than individual elements inside the mockup. Some teams might struggle with this approach, others might find it to be an easy adjustment to adopt.

Consumer-experience for adjusting icon size with modes

But even if your consumers are cool with this set up, there's a catch: icons aren't the only components with multiple sizes.

You will be tempted to broaden the scope of the "Icon sizes" variable collection to just be "Sizes" so you can control other elements'—like <span class="figma-component">Button</span>—size using modes too.

a "button-left-padding" number variable in a "Sizes" collection

Look closely at that <span class="inline-code">button-left-padding</span> variable.

Notice that in both "default" and "large" modes it has the same value of 32px. In this example, the system only wants <span class="figma-component">Button</span> to exist at 2 sizes: "small" and "default." We can't leave mode values empty, so the best thing to do for <span class="figma-component">Button</span>'s large mode is to repeat the neighboring value that is "legal". That way if a consumer sets an instance of <span class="figma-component">Button</span> in "large" mode (not allowed), it will look identical to medium (allowed).

Some will want to play devil's advocate... "Alice, you could get around that by having a "button sizes" collection with 2 size modes, and a "loaders sizes" collection with 4 size modes, etc etc."

But that will quickly become untennable when you need to nest icons inside of those elements.

Imagine that nested instances of icons need to remain in small mode when <span class="figma-component">Button</span> is in both small mode or default mode. But in large mode, <span class="figma-component">Button</span>'s icon needs to bump up to default mode.

Buttons of different sizes using Icons at not-necessarily matching sizes, from Nathan Curtis' Size In Design Systems

This is not possible to do when you handle size with modes without component-level variables. And so far, my experience tells me these should be avoided.

The number of variables you'd need, and clever aliasing you'd have to do, would get very hairy, very quickly.

Let's pause again. The point of this infrastructure is to control sizes of various UI elements. That's what we want. But as illustrated above, using modes to handle individual elements sizes does not offer better control than other approaches.

<div class="horizontal-rule"></div>

That covers 2 approaches I do not endorse. Let's move onto 2 that I do recommend.

Just okay: Icon wrapper components

I first learned about the icon wrapper method from Ridd back when he was sharing Figma tips in the early 2020s. This approach caught on and became quite popular among design system folk and Figma nerds.

What made icon wrappers attractive was they made it easy to manage a closed set of icon sizes—number values—from a single place: an <span class="figma-component">icon wrapper</span> component.

This approach was very much of its era. Base components were also popularized around that same period of time.

Hindsight tells me the popularity of these approaches was borne out of a lack of variables. Back then, the only feature we had for systemizing things were components.

But discussed earlier, variables are not the answer here.

The pain that icon wrappers addressed was about asset management. These components made managing "size" for icons easier.

Adding a new size? Easy. Changing an existing size? Easy. Deprecating a size? Pretty easy.

But in an age where automation and bulk-editing are getting increasingly easier, do icon wrappers still have a place in our library files?

Yes, I think so, but there's more nuance now.

Why icon wrappers are bad for UI library use

Again, consider building a <span class="figma-component">Button</span>.

If you're a library architect whose library is using <span class="figma-component">icon wrapper</span> to manage icon sizes, you'll nest an instance of it inside of <span class="figma-component">Button</span>.

Why?

Because consistency. <span class="figma-component">icon wrapper</span> is where the system describes what sizes are "allowed" for icons.

So you nest an instance of <span class="figma-component">icon wrapper</span> inside of <span class="figma-component">Button</span>, which itself has 3 sizes (small, medium, and large).

But this is where things become fragile.

To let consumers swap icons, you need to expose the properties on that nested <span class="figma-component">icon wrapper</span> instance. This will reveal both the "size" property" and "icon" property.

But remember the image from earlier with the teal buttons... the system likely wants icons within <span class="figma-component">Button</span> to use specific sizes. A "large" icon in a "small" <span class="figma-component">Button</span> is not allowed.

There is a popular idea proposed on the community forum to allow component architects to choose individual properties they want to expose. That kind of granularity would resolve this issue. But this seems like a complicated feature to release. Some properties are inter-dependent (see Nathan Curtis' "The Sorry State of States" article). If changing one prop reveals, hides, or changes other props, how would this feature work?

I think it's best to not put our eggs in that basket. Let's focus on what we can control, with existing features.

Pesky "size" property aside, this approach limits the design system from using other quality of life features across icon-having elements. Namely, preferred swaps.

<span class="figma-component">icon wrapper</span>'s "icon" property must offer every icon for it to be useful. This is bad news if the system only wants certain icon glyphs to be used in <span class="figma-component">Button</span>, or any other element using <span class="figma-component">icon wrapper</span>.

Perhaps this is less problematic for <span class="figma-component">Button</span>, but more so for <span class="figma-component">Rich text button</span> that only wants icons like <span class="figma-component">Bold</span>, <span class="figma-component">Align-left</span>, <span class="figma-component">Underline</span>, and <span class="figma-component">Paperclip</span>.

Better: "Size" variant property on each icon component

I've written at length about how costly variant properties are, but considering aaallll of the above, I believe they're our best bet for managing "size" as a property (icons or otherwise).

"folder star" icon component with 6 sizes

Having the "size" property exist on every icon component delivers a better experience to consumers:

  • You can expose an icon instance's "size" property when appropriate, rather than being forced to (like with the icon wrapper method). This reinforces the idea that if a consumer sees a property, they are allowed to configure it to their needs.
  • Components that use icons can own their own "icon" property that lets consumers change icon glyphs. This also allows component architects to set perferred swaps tuned to that specific element (eg. <span class="figma-component">Rich text buttons</span>)
  • Going back to the first benefit, mismatched sizing between icons and a parent element no longer pose a problem. If small and medium size <span class="figma-component">Button</span>s use small icons, but large <span class="Button">Rich text buttons</span>s use medium icons, that's all fine. And consumers don't have to be burdened with

But this method comes with a cost.

It is more effort to manage a "size" property across hundreds of icons. Adding a new size, changing an existing one, or removing a size, are all going to be moderately effortful. I tried looking for a reliable plugin, but came up empty.

But I'm hopeful that new technology will make those tasks easier. Folks who are using TJ Pitre / SouthLeft's Figma Console MCP (I am not, as of writing this) may be able to get help writing a script that assists with those tasks. I will try this myself (soon, hopefully). I'll update this post when I learn more.

Best (maybe??)... faking an icon font?!

I also have an idea of using a hidden text layer inside of each icon, and controlling the icon's size by adjusting that text layer's type style..... but again, I need time to play with that. And I'd rather not hold up publishing post. So I'll leave it as a teaser for now. Maybe you'll try it before me! If you do, tell me what you learn. Happy to credit folks in future updates to this post.