Fancy and flexible CSS outlines to help web accessibility

Fancy and flexible CSS outlines to help web accessibility

You might be using Bootstrap, or maybe even never considered outlines. Or deliberately removed them, leaving your users in the dark. However, outlines or focus indicators can still be fancy and helpful at the same time, without impacting

This article is part of the Dutch week of digital Accessibility and is a follow-up of introduction to outlines and focus ring.

Bootstrap has focus ring accessibility covered

The Bootstrap CSS framework took care of keyboard accessibility already. For dropdown or collapse components as well as button outlines. Next to the need for outlines to be visible, to be WCAG compliant, a custom created focus ring should have a minimum contrast ratio, or should be at least two pixels of width.

Below, you see an example of two coloured buttons as styled within the Bootstrap CSS framework. The button indicator meets the G195 technique.

Bootstrap button outline contrast

When being bad-sighted, you might have a challenge to distinguish focused from non-focused buttons, while the outline itself is too vague. But what if there was only one gray button displayed within the current viewport? You aren't able to tell if it received focus if you don't know the default style and thus don't have any reference. I wanted to address this on our websites for a while already.

One of the websites was the website of my very own web agency, Blue 2 Blond. And when it comes to outlines, we were inspired by Dutch Accessibility Foundation website, as they just launched a new and slick website as well.

Our outline inspiration

They used a border to simulate a padding between the actual button and a focus ring, officialy called offset when talking about outlines. Next, they used a box-shadow to create the actual focus ring. A box-shadow is convenient as it will adhere to border-radius property and it can even be animated (although I have to indicate that animating all properties by using transition: all; isn't a good practice from performance perspective).

This is the result, including a bit of code:

The issue with this approach

So, we spied on them and wanted to achieve the same. But as front-end developers, we would have to know the background color of the surface in advance. This is what they did via the .bg--quaternary .button-tertiary selector. Not ideal if you want to maintain flexibility, for example for content writers who don't know the background color of the parent element in which the button will be placed.

You could write more CSS to cater for all background and button combinations, but this would require more CSS and still reduce flexibility. So, this wasn't going to work for us.

Our tweaked outline solution

We then came up with a different approaching achieving the same visual outcome, with all disadvantages taken away. First of all, for all basic links which weren't buttons, we just used the outline property. Within footers or dark-mode, we switch to white outline colors instead of blue.

Steps taken towards improved CSS outlines

Then, we addressed our buttons as following:

  1. We removed the outline, but only on buttons as we will be recreating a focus ring for buttons only;
  2. We created an extra element within each button, using the ::before pseudo-element;
  3. We declared an empty content property to make it part of the button's inner contents;
  4. We don't want it to change the button's inner dimensions, so make it an absolute element in terms of CSS;
  5. To make it respect its parent button dimensions, we have to specifically set a position property to this parent. We added position: relative; to the .btn selector within our stylesheets;
  6. Now, apply top, right, bottom, left properties to the before-element, all with value zero to give it the same dimensions as its parent button;
  7. You can now declare a box-shadow to the ::before pseudo-element and use a negative margin to create a natural offset;
  8. currentColor is a CSS variabele that was even supported by Internet Explorer 9. It will automatically be assigned the color applied to the color-property of the same or closest element. By default, use currentColor in your box-shadow declaration;
  9. Chances are, you will now be having a white focus ring for .btn-primary or .btn-danger buttons as their text is white, probably only making the focus ring visible in dark-mode;
  10. If your website does not have a dark-mode, then we just created an accessibility issue, as a white focus ring won't be visible on a white background. But we only have to change the color property to make it work on light backgrounds as well, without having to declare the box-shadow property all over again;
  11. Bootstrap for example also has button styling for inverted colors, making the button's text color red, as well as the border, but making its background white. Quite confusing, but Bootstrap is calling them .btn-outline-stylename.
  12. By re-using those color properties, we only have to add our before selector to each color-specific outline declaration. Using any build tools for CSS variables is up to you. An accessible outcome for our users is what we are focussing on (pun intended);
  13. We can know git rid of Bootstrap's custom focus rings created with box-shadow, reducing the CSS size. Especially this part made it feel like a cleaner and more flexible solution.

You can see it in action in the screenshot above, containing three buttons. One is a green Bootstrap button, the second one is the same, but including focus. The third button is an inverted version of the green button. By re-using the style declaration of the inverted button, we are declaring the focus ring color for green buttons, while the focus ring for green inverted (or outlined as they call it) buttons has already been declared by the color property within the .btn-outline-success selector-block.

View demo and code examples

Increased ring focus flexibility

We can now style our very own custom created focus ring any way we want. For example adding a border radius, changing the offset using (negative) margins and still being able to apply a box-shadow to the button itself. All without having to consider the given focus ring color per button color variant.

You could even make it partially transparent using opacity, animate it using transition properties and not impact browser performance while animating the focus-ring, as opposed to animating the box-shadow (because in the end, I am still concerned about pagespeed and performance).

And if your website has or will get a dark-mode like Blue 2 Blond has, it is a 5-second job to overrule the color of your custom outline, by setting the color or border-color property to white.

Happy coloring and outlining while keeping optimal focus! 😉