In this article, we'll see how to make fully accessible icon buttons using SVG and CSS, without bloating the html with the inline
<svg> code but keeping the customization!
But first, let's define what we want to achieve with our code, what we want from the final output of our icon button element:
- Keyboard controlled
...and we will achieve all of these points with just HTML and CSS! It's magic, isn't it?
<button> element is everything we need, you never would have said that, right?
<button type="button" aria-label="Download File"></button>
If you don't like to use an empty element, you can use your
.sr-only utility class to hide the inside content from the UI.
<span class="sr-only">Download File</span>
You can choose to use the
aria-label attribute to add a mean to the button or the hidden element readable only by assistive technologies, it's up to you. Using the
Interactive Keyboard controlled Accessible
So, how we can change colors, sizes, add gradients using a single SVG image from our CSS? The answer is...using the very wide supported
The only constraint is that the source icon should be black on a transparent or white background. That's because the mask property uses the pure black color to shows the visible part of the background, while the white or transparent color is used to obfuscate the background layer.
Let's start adding some basic style to our button to set up positioning and box model:
Now, we are going to use a
::pseudo-element to add the icon. The absolute positioned pseudo element is useful to mathematically centering the icon, but at the same time, it allows us to optically center the icon when we need to do it.
/* Icon positioning related code */
/* this allows optical centering if required */
transform: translate(-50%, -50%);
We can now add our magic code. First of all, we need an icon, and we need it as SVG since we want it to scale without losing quality. We will use this icon for this example.
mask- properties we can set our icon as element mask as we do with design tools like Sketch, Figma and others. Let's add these properties:
/* Previous code... */
/* Masking code */
As you can see we defined four things here:
- The mask url: our black svg icon
- The mask position: set to be always centered
- The mask repetition: we disabled the tile effect.
- The mask size: we set it to be contained into the available space, so the icon will be always fully visible.
You might have noticed that these properties are the same (and act like) of the
background- properties. The main difference here is that the image is used as a mask and makes our element "transparent" where the image is black, while the white (or transparent) part of the image is used to hide what's on the background. Our icon here is white because we set
background: #fff; on the pseudo element and the svg icon is filled with
Now, we should see something like this:
This way you can change the background, add gradients ad make fading-out icons by filling them with a gradient from black and to a transparent color. Inside the
mask-image you can also use an icon from your sprite SVG file! Pretty awesome and scalable!
Let's see some example using different icons and changing the
The first button uses a full black svg icon, the second one adds a linear gradient to the
.setProperty(), for example:
We can now flag the last step! 🎉
Interactive Keyboard controlled Accessible Customizable
mask- properties are well supported by all the modern browser, but if for some weird reason you need to support obsolete browsers that don't support these properties, you can progressive enhance the code in order to provide a minimal UI experience.
Be aware that when talking about "minimal UI experience" we mean that things may not look the same, but they just work as expected without breaking the user experience. It doesn't mean "add a ton of code for old browsers just to replicate the visual appearance of the element" because that code will be downloaded even by modern browsers, but never used.
Enjoy it! 🎉