Focusing on focus states

Some things I’ve learned about designing focus states at GDS, BT and LBG.

Dave House
12 min readSep 23, 2022

This is a living blog post — I will update it with more examples and clarifications over time.

What is a focus state?

In simple terms a focus state is a visual indicator of what interactive element on a screen is currently selected.

This means it is a useful orientation device when navigating between interactive elements of a screen using a keyboard.

Pressing tab, will move the focus along to the next focusable element on the screen so a user can be confident they are interacting with the element they wanted to.

In some ways, you could consider :focus to be the keyboard equivilent of :hover to a pointer device like a mouse. Conceptually, in some scenarios this isn’t wrong. However, the impact of :focus is much greater than :hover for the people that rely on it to navigate a screen. It also has specific requirements in both WCAG 2.1 and the upcoming 2.2 that :hover does not have.

Who benefits from focus states?

All of us benefit, but some of us rely on them.

Often we only think about :focus the state that a keyboard user will see when navigating. However, everyone designs focus states all the time we just might not always equate them to being :focus.

For example, designers may design what they perceive to be the “selected” or “active” state of an element when in reality this is :focus. Selected and active generally mean different things, although confusingly there is some crossover.

The state of a text input when you select and type into it is :focus. This is a useful orientation device for everyone to help them understand which input in a form is currently selected. It doesn't matter if you clicked into it or tabbed into it.

When you’re scrolling through Netflix or YouTube on a smart TV using a remote, everyone relies on what is essentially a focus state to understand what TV show or video they are about to select. Often on these kinds of interfaces the designers use a ring around the tile, make it grow in size or overlay other information.

Imagine you have to navigate a website in the same way you navigate Netflix with a TV remote and you might understand why designing good focus states and doing thorough testing with a keyboard or switch device is essential to supporting users that navigate a screen in this way.

Browser defaults and WCAG

Many people decide to not design :focus states because they perceive this as being something that is handled by the browser. They opt to rely on the default user agent styles in the thought that, even if they are not perfect a user that relies on them will probably have experienced them before.

For many years this was something that was considered the best approach. Some people may still argue for it.

The argument against doing this and creating custom focus states for me is laid out in WCAG 2.1 (and more specifically in 2.2).

To paraphrase and simplify, user agent styles are exempt but custom styles are not.

If you were to create custom focus styles that replicate the browser default styles directly, they would fail Non-text contrast and Focus appearance in 3 of the 4 major browsers. It’s one area that Microsoft IE and Edge have always been better at out of the box.

The exact same design, can be both exempt (therefore pass) and fail WCAG.

For example, if your organisation wanted to use “default focus states” but through some kind of mis-judged consistency decision wanted to align them all around the design of Safari, recreating the Safari focus ring in CSS would fail WCAG in all browsers including Safari.

It’s not the design that makes it custom, the decision to set a custom :focus style means the design is no longer exempt. This means it needs to meet the requirements laid out in Non-text contrast in WCAG 2.1 and more specifically in Focus appearance in the upcoming WCAG 2.2.

So, if we don’t believe the browser default focus styles meet basic accessibility standards should we ever use them?

Custom styles can make things worse too

One of the push backs on creating custom :focus styles is that at least browser defaults are somewhat standardised and they work as users expect through previous experience. For example, out of the box they just work when users modify their way of viewing a screen, such as using high contrast mode in Windows.

Sometimes when custom :focus styles have been designed and developed they have frankly, made the experience worse.

Because the outline property in CSS is a bit basic many users opt to style :focus with other CSS properties like box-shadow and background. They can’t use anything like border because that has an impact on the real size of the element.

Back in 2019 I attempted to make the case for outline-{{direction}} in CSS to allow people to create focus states that did not have to be a complete ring without needing to use something like the box-shadow hack. I’m not sure that went anywhere, although I thought it was a good idea.

I’ve also seen custom focus states that are no better than the browser defaults at passing basic contrast requirements, in this instance again — sticking with the default style would have probably been better.

The worst thing you can do

If you look at a focusable element in your codebase and you see the CSS “outline: none;” or “outline: 0” you have committed what I believe to be the worst mistake you can make when designing custom focus styles. Be aware that some UI libraries, including the most popular one in the world may have made this decision on your behalf, you might not even know it’s there.

Remember, some users rely on focus states to navigate. These users are also more likely to make other adjustments to their browsing experience.

Settings like overriding colours in Firefox or Windows High Consrast Mode will remove or override certain styles from your stylesheet. It will for example 100% obliterate any use of box-shadow.

If you have used box-shadow for your focus ring and set outline: none in your CSS the user will have no focus ring at all. I’m pretty sure this is the last thing you wanted to happen.

The fix

A simple fix to this is to make sure you have an adequate fallback in place. That fallback can be as simple as defining a transparent outline.

This trick has long been used for this purpose, yet I see examples of outline: none every single day.

By styling your element’s focus state with a transparent outline you are giving users that override colours a fallback that will be recognised without impacting the box-shadow based design.

.myElement:focus { 
outline: 2px solid transparent;
}

It may not be obvious but by defining the outline as transparent, when those colours are overridden it will override the transparent value into to a colour. It cannot make outline: none a different colour, because you’ve been explicit that there is no outline.

Nick Colley went one step further with this approach (for example, to create fallbacks for when you have used box-shadow to make custom radios that will become invisible or visually un-checkable) in the blog ‘Supporting users who change colours’ while at the Government Digital Service.

What does good look like?

Colour contrast

It is vitally important that your :focus state has good contrast with every single colour that an interactive element can sit on top of. Following WCAG this should be a minimum 3:1 contrast ratio.

This means a single colour for focus is rarely possible unless you have a single colour background throughout your entire website.

One thing to bare in mind is that you do not have to rely on the same element of the focus state design to meet contrast. For example, the focus state used for hyperlinks on the GOV.UK Design System worked on multiple backgrounds, but for different reasons.

An example of the link focus state for GOV.UK working across all backgrounds used on the website
An example of the link focus state for GOV.UK working across all backgrounds used on the website

This single design approach means the yellow background has contrast with dark backgrounds and the combination of 1) removing the text underline and 2) adding the thick bottom border (created with box-shadow) creates something that is a significant enough change from the default link style on light backgrounds.

On light backgrounds the yellow is just an enhancement, while on dark it’s essential.

So, some methods to consider when designing :focus states might be:

Double outline

The idea of using a double outline for focus rings (achieved with 2 box-shadows) is that one of the rings will meet contrast on any background.

Often you can design these in a way where it’s not even evident that they use a double ring. For example, if one of your rings is white it’s not going to be visible on a white background, if the other ring is your main dark background colour then it’s not going to be visible on that background either. This means in most settings the focus state will show a single ring and you can define the :focus state only once for all similar elements.

Don’t forget the outline fallback!

An example of a double outline focus state on 3 different background colours
An example of a double outline focus state on 3 different background colours

Multiple elements

Much like the GOV.UK style, this is when two or more elements make up the :focus state and that ensures the state as a whole passes contrast when assessed against different elements of the design.

The draw back of this approach is that a user is not getting a universal style. For example, on GOV.UK if you cannot see the yellow colour, the focus state on light backgrounds is a thick border, the focus state on dark backgrounds is that a background colour appears (and in both cases removes the underline). Although this wasn’t found to be an accessibility barrier in user research, it has potential to be one, especially if your website has a higher information density and uses more colours than GOV.UK.

Stop thinking about colour alone

Thinking about shape can be a useful tool when designing :focus state.

Put any thoughts of a :focus colour to the back of your mind and design a state that makes changes to scale or shape in a significant enough way to be recognisable in monotone.

Techniques to try could include:

  • Making the element bigger (or appear bigger)
  • Changing the thickness of an existing border (or fake border)
  • Inverting colours
  • Changing border radius
  • Adding extra elements, such as a marker

If you successfully create something that works in monotone, pairing it with colour and a traditional focus ring if relevant will enhance it even further.

Examples

This example of a text input shows the border is made thicker and darker. Because this border change alone makes a recognisable “physical” change to the appearance of the element this would be an adequate focus state.

It could then be enhanced with colour. On the bottom right of the image below, the light blue border, what you might normally consider to be the “focus ring” does not meet adequate contrast. However it is purely an enhancement to the border width change, which does.

An example where the border of a text input is made thicker and darker
An example where the border of a text input is made thicker and darker

If you used the light blue focus ring without the change to the border width it would no longer be adequate. In this instance you are relying on the colour of the focus ring as the signifier of the state.

While the above design was certainly acceptable, could you do more?

In this next example, the state has been designed to have both the border width change and a focus ring using the double border technique.

This design works better in monotone and translates directly to colour.

While either of the 2 changes (from the default state) would technically be enough, applying both makes it more robust and gives you a few more potential styling options.

An example where the border of a text input is made darker and thicker and a double focus ring (that meets 3:1 contrast) is added
An example where the border of a text input is made darker and thicker and a double focus ring (that meets 3:1 contrast) is added

Another technique to try would be where the focused element appears to grow.

While you could actually make it grow using css scale this technique gives the appearance of growth without the element getting any bigger or zooming in.

In this next example the focus state is styled much like the previous design, but in this instance the original border is made transparent on focus rather than thicker. The focus ring is then used as a fake input border.

To the user, entering this input it would appear that the input grows around 6px on each side and the border also appears to get thicker.

Here when adding colour, I have brought back the light blue as an inner shadow. I feel this is a nice balance because “the work” of the focus state is done on both the “border thickness” and the “grow”. The use of the inner shadow is purely decorative.

An example where the border of a text input is made transparent on focus, the box-shadows give the appearance of the input growing and the border getting thicker.
An example where the border of a text input is made transparent on focus, the box-shadows give the appearance of the input growing and the border getting thicker.

Keyboard only focus styles

Although this has been possible for a number of years through hacks or CSS polyfills it is now much easier to provide an alternative focus style for keyboard users.

Using the :focus-visible pseudo class allows you to set specific CSS for elements that have received focus through keyboard navigation.

Simply put, if you tab to it you’ll see the focus styles, if you click in it you won’t.

Types of focus

Whether or not you use :focus-visible is not a binary choice.

Some elements, like inputs need a visible focus state whether focused by a keyboard or a click / tap.

Some elements like buttons may benefit from having a keyboard only focus state.

I categorise this into two buckets: “transitional” focus and “retained” focus.

Transitional focus

Elements that have transitional focus are things that, for example take you to a different page or submit a form. When you click a link or button with a mouse it is both :focus and :active while the mouse is “down”. As soon as the element is clicked you are taken somewhere else and focus is reset.

Retained focus

Retained focus is the opposite. When you click into an element like a form input the focus state is retained until you leave it.

My suggestion is elements that have transitional focus, which often conflicts with :active states should use :focus-visible. The ones that retain focus should use :focus as normal.

Using focus and focus-visible together

One option that could be considered is using :focus-visible to enhance the default :focus style even further when using a keyboard.

Rather than only having focus styles for keyboard users you could make the decision to just style them differently.

For example, if we mix up some of our designs from above, the focus style on the left, which still passes WCAG (on this background) could be set on :focus and the design on the right could be set on :focus-visible as an extra enhancement.

An option to style :focus and :focus-visible differently to give a keyboard user a stronger focus style.
An option to style :focus and :focus-visible differently to give a keyboard user a stronger focus style.

This approach can sit in a moral grey area if abused. If you could make the focus state more accessible for everyone shouldn’t you just do that?

Firstly, the default focus style should still always meet the appropriate WCAG guidelines. It should always be accessible and more accessible, not inaccessible and accessible.

However there are always things at play in organisations that you can’t control. How many of us have been asked to “remove the ugly blue outline” in our career? Have you always pushed back? Even as junior? Even when you’re not doing well in your org?

If this approach means you can sneak in more accessible focus styles for keyboard users without affecting any of the current designs then I would argue this is a useful first step.

If you’ve read all of this and you’re not sure where to start, I recommend:

  • Spend some time using a keyboard to navigate your website — If you’re on a mac Chrome will just work with the tab key, Safari and Firefox need settings enabled
  • Check focus states are visible as you navigate through actionable elements
  • Test your website by overriding colours — are the focus states still there?
  • Make focus states design and behaviour part of your design process

Native app caveat — This blog is very web focused. When working with native apps you may only need to design focus states for elements that “retain” focus. It is my understanding that even when a native app is using an external keyboard it will use the OS level focus states you might be familiar with when using VoiceOver or TalkBack.

--

--