Go to homepage Skip to main content Skip to keyboard shortcuts definition Skip to table of contents Skip to community section Skip to guide
How to use websightly?

Don't disable outline, master it instead!

Estimated read time: 42 minutes
Last updated: 5/22/2020 9:18:27 PM

Too long, didn't read

Here's a summary of what I wanted to share with you today:

  • Always provide styles that indicate focus state, the method does not really matter
  • If you don't have time for reimplementing focus indication, leave outline as it is (avoid outline: none)
  • Be careful with overflow: hidden as it may crop the outline
  • Use customised outline/indication, if the default is not sufficient on your site

I strongly encourage you to read the whole article though, enjoy!

Author's notes

If this is your first time on Websightly, you may want to take the guided tour. If you find some sections to be uninteresting to you, you can collapse them by clicking the button in the section name.

I would like to thank my friends Jakub for various brainstorms regarding the topic and Carlos for proofreading.

Introduction

This article is about the outline CSS property, or rather the lack of use on modern websites. This isn't the browser vendor's fault though. The fact that there is just a fraction of sites that use it is on us developers.

What is outline?

CSS outline is a property which allows you to highlight element on the page that is currently focused by user. Meaning, if you edit text within a <input type="text" aria-labelledby="test"> element, you will have some kind of visual indication that this is the field that recieves input. It shows the user the current location on the page, while using the keyboard.

The juicy code-related stuff start in The story of outline chapter.

Why should I care?

Well, the most basic reason should be the fact that you are using some kind of keyboard while surfing the web. And even if you're not, any other kind of input device still relies on the focused HTML element.

If that's not enough for you - take accessibility into consideration. There are countless users of the web, and some of us have disabilities. I for one cannot really read without my glasses, and I'm relatively young at the time of writing this article. Others cannot use a mouse or trackpad because of their motor disabilities.

You don't have to be disabled to be in their shoes - I remember times when me and my family were just getting a grasp over the internet and personal computing. We were using a mouse with one of those heavy rubber balls, and one day - the mouse broke. Oh, well. The computer was unusable for few days until we bought a new one - and that could happen to you or your users too. If you're using a laptop, your trackpad could break and you may not have a spare USB or wireless mouse to connect. What then? Maybe you won't be able to play FPS games, but you should still be able to surf the web to access information.

(And yes, in this scenario we assume that your smartphone blew up as well and there's no other way to access the Internet than via PC. Or imagine yourself at an internet cafe in a foreign country. You don't have access to roaming and they don't have a mouse. Seems abstract, I know)

Personally, I didn't know the power of a keyboard fifteen years ago - I didn't know what the tab key was for. Now I know that with it you can access interactive elements, both in browser and outside of it, and I believe I am quite skilled with keyboard shortcuts - I very rarely use trackpad to navigate over my OS nowadays.

So why should you care? Because a keyboard is a valid form of navigation that people use, and you might use it as well. The outline serves one purpose: visual indication of the active element in the browser. Without it, you can only guess where you are and what you should do.

The story of outline

Let me tell you a brief story of outline. It will include a little bit of browser history and why we are where we are today.

The birth of the CSS property

According to Can I Use outline was first implemented in Firefox 2, released in October 24th, 2006. It was a long time ago and the web was much more like the wild west back then, but outline was supported well from the very beginning. After the Firefox release every browser supported outline, to some extend at least.

Data on support for the outline feature across the major browsers from caniuse.com
Browser support for outline provided by caniuse.com

Basically, this allowed you to do something like the following:

Permanent outline
List of files in the snippet editor
<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="utf-8">
  <title> Kurs flexbox | Koduje </title>
  <link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet">
  <link rel="stylesheet" href="client-variables.css">
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <section class="card-section">
    <div class="card card-1">
      <div class="plate">
        1
      </div>
      <p>
        Example card description
      </p>
    </div>
    <div class="card card-2">
      <div class="plate">
        2
      </div>
      <p>
        Example card description
      </p>
    </div>
    <div class="card card-3">
      <div id="something" class="plate">
        3
      </div>
      <p>
        Example card description
      </p>
    </div>
    <div class="card card-4">
      <div class="plate">
        4
      </div>
      <p>
        Example card description
      </p>
    </div>
    <div class="card card-5">
      <div class="plate">
        5
      </div>
      <p>
        Example card description
      </p>
    </div>
    <div class="card card-6">
      <div id="else" class="plate">
        6
      </div>
      <p>
        Example card description
      </p>
    </div>
  </section>
  <script src="script.js"></script>
</body>
</html>
/** **/

.card-6 {
  outline: 5px solid var(--current-color);
  outline-offset: 5px;
}

/** **/
body {
  padding: 0;
  margin: 0;
  font-family: 'Montserrat';
}
  
.card-section {
  background-color: #f7f8f8;
  height: 100%;
  width: 100%;
  padding: 5px;
  box-sizing: border-box;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.card {
  --current-color: var(--example-purple);

  height: 130px;
  width: 100px;
  border-radius: 3px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  margin: 10px;
  
  color: rgba(90, 92, 107, 0.85);
  text-align: center;
  font-size: 0.5em;
  overflow: hidden;

}

.plate {
  height: 70px;
  width: 100%;
  line-height: 70px;
  font-size: 36px;
  
  color: #ffffff;
  background: var(--current-color);
}

.card-2 .plate {
  --current-color: var(--example-green);
}

.card-3 .plate {
  --current-color: var(--example-orange);
}

.card-4 .plate {
  --current-color: var(--example-pink);
}

.card-5 .plate {
  --current-color: var(--example-olive);
}

.card-6 .plate {
  --current-color: var(--example-blue);
}
:root {
    --example-purple: #997fbc;
    --example-green: #92ded2;
    --example-orange: #fdc28a;
    --example-pink: #ee8ebc;
    --example-olive: #babc7f;
    --example-blue: #7f7fbc;
    --example-background: #f7f8f8;
} 

And while I don't think it's very useful on its own, outline started to shine with the :focus pseudo selector. Any element that was activated by the user (via use of the cursor or keyboard) could now be styled to inform the user what was actually going on and what element was actually being modified.

But then something happened. For some reason outline was (and still is) not enabled on majority of websites on the Internet, and people started to notice it. What was it?

The reason behind its lack of use nowadays

The reason is the fact that web development is mostly a business. And if the main motivation behind making websites is money, then people omit mechanisms they might not understand (and may think they don't need) so they can close the projects as soon as possible. Stakeholders want profit and consistency, and unfortunately the trend in that everything should look exactly the same across all devices and browsers is still present nowadays. Given that styling was much harder ten years ago than it is today, we had to unify the behavior of various browsers with some standarised set of rules to keep our sanity! And so CSS resets were created. A set of rules that you would include on your page, and then start styling to match the design. And these stylesheets included a rule that disabled outline by default.

This is most likely because it was not aesthetically pleasing for designers / CEOs. Plus, people didn't understand what it was for. If you clicked on a button with your cursor, using mouse of trackpad, the button would probably do what it was meant to do. But it would also get this weird focus ring that didn't match the design!

The user knows what they clicked anyway, right? So let's get rid of the outline - people may think.

This issue was outlined on the famous outlinenone.com. The site's first indexing made by web.archive.org happened on October 4th, 2010 - almost four years after outline implementation in Firefox 2.

Author states:

Many designers use CSS resets which include styling that removes the outline. One of these is Eric Meyers well known CSS Reset, but Eric makes it perfectly clear that users should redefine focus styles.

and that

Usually "design vanity" or total ignorance of accessibility

is the reason of why do some web designers remove it.

He also mentiones that if you really have to remove it, provide an alternative styling:

There's no real reason to remove the outline, but if your life depends on it there are a number of alternative methods to style links that are in focus [...]

Years went by, yet only a few people actually implemented an alternative focus indication after applying the infamous outline: none. I think that most people didn't do it intentionally though - CSS resets were everywhere, and junior developers who simply didn't look at the source of such resets and who didn't know the purpose of outline simply missed this case. And we can see the results everyday.

Let's play a game: pick 5 sites that you use on daily basis. Visit them and try tabbing through their content. The more they present some kind of focus indication, the more points you get (feel free to share your score if you want)

The present and the future

We can do tons of things with outline right now - change the color, offset, make it round, animate it with the use of transition CSS property or @keyframes, and there are even tools for creating complex animations between current active element and the next one.

There are even pseudo-classes that will allow easier management of focus state indication.

Pseudoclass: focus-within

:focus-within (see more on MDN) allows to style an element within which there's focus. In the following example, we'll set up a section that will change its background color when the input element within it is focused:

Example of focus-within
List of files in the snippet editor

As you can see, the card gets the styles defined in .card:focus-within once we focus on the input that is nested in this element. And ofcourse, it doesn't necessarily have to be outline property - it can be anything you think of, but its purpose is to style elements within which there's a focused element. It's especially handy with complex forms with complicated markup.

Pseudoclass: focus-visible

:focus-visible (see more on MDN) is a pseudoclass that is active when an element is focused, and when the user should see a focus indication (this is determined automatically by the browser, based on the UserAgent of a client).

Data on support for the css-focus-visible feature across the major browsers from caniuse.com
Browser support for css-focus-visible provided by caniuse.com

Though as of writing this article, it is not well supported as it is in very early stage of specification process ( see more on CSSWG ) - it can only be used on Firefox, with the use of -moz-focusring. The standard syntax won't work anywhere just yet.

Example of focus-visible
List of files in the snippet editor

This could be handy if you only want to show focus indication if the user is keyboard navigating. If you visit the example above from Firefox, you can try clicking on each button. Then try to tab through it - the styles should be applied only when tabbing.

Still it's worth knowing that there are ideas of such mechanism for us to use later, so we don't have to hack it and use native solutions.

Creating the structure of our example page

In this section we'll create the structure of our site from scratch. You'll be able to see the steps, starting from raw HTML markup into fully styled example on which we'll work further on.

Creating the HTML markup

In our example we'll want to have a list of card items that will be - ultimately - a link. Since we want those to be as simple as possible (because outline is the topic of the tutorial) they will simply contain a card number and some text. These will later link to the relevant sections. But first things first, let's create a raw HTML with every asset files that we might want linked, and a basic structure for our card list.

To generate the card list we can use emmet given following formula: ul.card-section_ul>li{$}.card.is-card-$*6

Structure coding - basic markup
List of files in the snippet editor

Cool. So now we have a list of cards with hardcoded card number. Additionally each card has is-class-$ where $ is its number. We've also loaded a Montserrat font from Google Fonts and modified the default background color. Let's give our cards some shape.

Structure coding - giving cards its shapes
List of files in the snippet editor

Right, next let's distinguish each card with a plate - we'll add a div into each of our cards with a class .card_plate that will differ in color based on what card it is. In the previous snippet I've linked client-variables.css file which is a stylesheet that I'll use in various of my examples - there are some CSS Custom Properties already defined - let's apply some colors then!

Structure coding - some colors
List of files in the snippet editor

Nice. Notice how well CSS Variables provide verbose and a convinient way to manage your styles. You don't even need SCSS or other CSS preprocessor to have a unified color palette on the web nowadays. It's as simple as background: var(--example-purple) ๐Ÿ˜Ž

But, the plate is a little small and you cannot really tell which card it is. Let us fix that with proper height and font-size. Remove the default ul margin and padding from .card_plate and basically - fine tune it so it's "visually pleasing".

Structure coding - visual improvements
List of files in the snippet editor

Alright, let's say we're done with .card styling. There's however a little issue with contrast in the following example - as this isn't the intent of this tutorial we'll ignore that, but keep in mind that providing contrast of at least WCAG 2.1 AA Level is the right way of doing this in regards of inclusion and accessibility.

We'll have to make it a valid link now, so we'll wrap the current contents of li with proper a tag that will point to the card's section. We'll add the section that those point refer to in a minute.

Structure coding - making our card a link
List of files in the snippet editor

As we can see we have to update our example to reflect styling that we got from a tag. Surely, if we used CSS Reset mentioned earlier in the example it would be much easier, but for learning purposes I want to keep it as from-the-start-til-the-end as possible.

We'll remove the text-decoration: underline from the links now and create sections to which they point.

Structure coding - disabling default link styles
List of files in the snippet editor

We have also made sure that the link takes up the whole height of the parent.

Cool! We have everything we need now to talk further about outline so let's just quickly create the sections the links will need and we're good to go! ๐ŸŽ‰

Again we can use emmet to generate markup quickly for us using following formula: section.content-section.is-section-$#section${content here}*6. I'll use social good ipsum for generating some dummy text within it.

So each section will have a class of content-section and is-section-$. But, more importantly, each of them will have its own unique id to which our anchor cards can now point.

For example purposes we'll also add some basic input at the top of our site.

As a finishing touch, let's move some of the cards styling from li itself to the link that it contains - because, right now, we've unintentionally hurt the default outline. But more on that later.

Structure coding - finishing touches
List of files in the snippet editor

Default focus indicators

Okay! Now that we're finally done with creating a basic example we can try to use the site we have created. Click on the Structure Coding - finishing touches preview an try to navigate the page using the Tab key on your keyboard - and interact with links by pressing Enter.

As you can see, there is certainly some kind of way that shows you on what card you currently are.

On recent versions of Firefox, you'll get a border-like dotted outline, and on Webkit/Blink browsers you'll probably get the familiar blue focus ring.

And the best part - we got it for free! It's simply because we did not explicitly remove it from any element that has focus. That's why initiavies like outlinenone.com exists, because it works by default.

Inefficient by default

But still - focus indication by default is far from perfect. Amongst many reasons such as aesthetics, the most important one for me is contrast. If you, by any chance have to develop a site that has a blue background for example, the default focus indication will not be easily visible.

On some elements, it might get cut. For example if we use overflow: hidden to crop our element sizing to its border, simiarily to what we did in our card example, the outline might not be visible even without disabling it explicitly. We can see this in Structure coding - disabling default link styles - there, a parent container (card) for the link has overflow: hidden and fixed size. Meaning that outline which is not restricted by bounding box will get cut. If you try to navigate with Tab in the preview of mentioned snippet, you won't be able to see it on most browsers, and very, very slightly on the rest.

That's why keeping the outline enabled is not sufficient in my opinion. Sure, it's better than nothing but we can aim higher than that, right? And if some stakeholders and designers also have trouble with it, why don't we please both ends?

Differences between browsers

Before implementing custom focus indication, let's talk about crossbrowser differences.

Depending on the combination of browser and operating system you use, UI elements and their outlines might be rendered differently. I will not go through all of the differences here, but you can test it out in this example - within the .native-inputs element there are various UI element on which you can test default browser outline for the browser.

Preview your browsers default outline
List of files in the snippet editor

To prevent unaware users from removing the outline on the elements, Mozilla Firefox supports additional layer of outline that cannot be removed with outline: none.

::-moz-focus-inner (Check moz-focus-inner on MDN) is Firefox specific pseudoelement that will add a characteristic dashed outline to focused elements. If you're aware of what you're doing, you can alter / remove it with modifying border property of ::moz-focus-inner.

For example, ::-moz-focus-inner { border: 0; } will remove Firefox specific outline. Usually there's no need to do that however as it's not very intrusive, and Firefox users are probably used to that and might rely on this.

Implementing focus indication, silently

So there are various ways to keep the outline both for those who need it and hide it as well as from those who do not want to see it. I'll present and discuss various approaches and talk about their pros and cons.

Class on body

One technique that I use is one that I learned from Jarek Rencz a couple years ago, and which happened to be the motor for further reading about accessibility. Basically, the idea is simple: detect keyboard and add a specific class that will enable outline.

  1. Hide outline by default on every single element
  2. Add event listener for keyboard navigation with Tab
  3. Add a class to document.body that'll indicate whether user is keyboard navigating
  4. Reenable outline on every element based on that class

So we'll do exactly that! First let's hide outline everywhere by default using infamous *:focus { outline: none; }. Yes, we can do that as long as we provide an alternative / enable it for those who need it. And chances are that if you're using some kind of CSS reset, it's already done in your project :)

Class on body - disable outline
List of files in the snippet editor

Now we can finally see the difference - if you try to Tab you have no idea on what element you currently are. Sure, if it's a link and you have a keen eye, on some browsers you can see the target href of such link in the bottom left corner of the browser. But for other interactive elements such as buttons or text inputs, you have to type/confirm and hope for the best, that you'll not submit some kind of form unintentionally.

So, we'll have to attach a js file now to handle the logic of reenabling it again if someone uses Tab to navigate. Let's write down a logic for that.

First we'll addEventListener to the body and listen for keydown event. Next, we need to look into ev.key and check if it's indeed a Tab key that was pressed.

If so, we can add class is-keyboard-navigating to the document.body itself!

Class on body - detect keyboard
List of files in the snippet editor

If you press tab whenever focused in the preview, you will see that the class is added to the body. But we still don't see the outline, so - lets add one final rule for this example to be complete.

Using .is-keyboard-navigating *:focus {} we can reenable outline only when someone is using keyboard navigation.

Class on body - style the class
List of files in the snippet editor

There are few issues with this approach:

  1. It's not really progressive enhanced - users with no JS will get no outline
  2. We're loosing the initial outline styling because we've disabled it in the first place (if we care about that)

The first one can be fixed relatively easy - we can keep the outline enabled by default in our CSS stylesheet, and append such styles with JavaScript. That way, user's without JS will still get outline, and if someone needs it - it will be shown after tabbing.

Class on body - outline enabled by default
List of files in the snippet editor

Of course, we could use various other techniques to disable default outline by default with JS - the bottom line should be that users without JS still get their focus indication properly.

The second point could be addressed by using the revert (MDN) property value on outline, although it is not supported very well and is still at Working Draft specification stage.

Data on support for the css-revert-value feature across the major browsers from caniuse.com
Browser support for css-revert-value provided by caniuse.com

Quoting from MDN:

The revert CSS keyword reverts the cascaded value of the property from its current value to the value the property would have had if no changes had been made by the current style origin to the current element. Thus, it resets the property to its inherited value if it inherits from its parent or to the default value established by the user agent's stylesheet (or by user styles, if any exist). It can be applied to any CSS property, including the CSS shorthand all.

Alternative would be trying to set outline-width: 0 instead of outline: none and reenable just the width of the outline to some value, though we cannot be really sure what was the initial width of that without revert itself. Providing custom focus indication tailored for the needs of our sites seems like a better option in this case.

Class on elements instead of body

If we want to provide custom focus indication with JS, following on previous examples we could start in following matter:

  1. Disable default outline only when the user has JS enabled
  2. Listen for focusin, focusout to grab the element that has recieved / lost focus
  3. Listen for keydown and keyup events to check whether this particular focus shift was made by keyboard
  4. Apply custom class to element that currently has focus.

This is something that Adobe Spectrum seems to do, after a glance at the DOM changes during keyboard navigation.

Let's try to reimplement this technique and talk about its pros and cons.

When it comes to progressively enahnced solution and adding new focus indication functionality when user can actually use it, we can use exactly the same solution as in Class on body example - simply add a style tag with JavaScript, that will set the focus indication to none.

Let's maybe focus (๐Ÿ™ˆ) on setting up the right listeners for focusin and focusout.

Implementation of focus-ring class approach

Let's get to work then:

Class on elements approach
List of files in the snippet editor

Okay - we've added outline disable when JS loads and set up event listeners to add .focus-ring class to any element that recieves focus. It all should work nice, but when we try to click / tab through the page whe won't see the outline. Why?

Well, specificity comes to play. While *:focus and .focus-ring has exactly the same specificty (see specificity calculator), the order in which the rule is present in our document also matters - and the rule that disables outline is currently placed after the CSS declared in styles.css file.

That's relatively easy to address - we just need to change document.body.appendChild(styleEl); to place the focus disabling rule somewhere higher.

Class on elements - fixing specificity
List of files in the snippet editor

Alright, now we can see the styles. But they still react to any kind of focus - regardless if it's triggered by mouse click, touch or keyboard. And we wanted to implement a keyboard-only solution, so let's add event listeners for keydown and keyup and store the state of that in separate variable.

Class on elements - reacting only to keyboard
List of files in the snippet editor

What have we done:

  • Events keydown and keyup on document (so on any element on our page) will set the state of isKeyboardNavigating variable to right value. The value will only change if user pressed Tab
  • Then, in focusin and focusout handlers we use the flag to update the element that this event refers to (e.target). This is conditionally based on the isKeyboardNavigating flag.

This can work like this because events happen in following order:

  1. keydown - when user is still pressing Tab
  2. focusout - if user is focused in any element
  3. focusin - if there's a new element gets the focus
  4. keyup - when user releases the Tab key.

Additionally we've wrapped the whole code in IIFE - immediately invoked function expression, so isKeyboardNavigating is not reachable from window scope.

Surely, this is a very raw implementation and could be improved - but you get the idea.

Is this approach worth using?

So, one of the benefits of this approach is the fact that you'll always get focus indication if navigating by keyboard. If you're not a keyboard user, the interface will not be "polluted" with focus styles, as the class is only applied when user is actually using the keyboard to focus on an element.

Contrary to the class on body example, we can now:

  1. Click on the element with a cursor. We won't get an outline.
  2. Press Tab on the keyboard. We will get an outline.
  3. Click on the element with a cursor again. No outline.
  4. Press Tab on the keyboard. Outline again!

With the class on body approach we'll enable outline once and for all, so further cursor selections will also have the outline visible. While I don't think this is bad, sometimes you may need the mechanism that this programatic approach gives you.

Additionally you can now animate each keypress on focused elements nicely, which Spectrum already does - but you don't have to go with this approach to achieve that. Using :active pseudoclass combined with an element that has :focus and applying CSS transition property will provide you with a very similar experience, without the need to mutate the DOM by applying a specific class to that.

This is a JS-only approach however, so without it we will either have every or no element outlined. But that was also the case with the Class on body approach.

Pure CSS solution

Luckily, in the near future we won't need JS at all to provide this kind of experience. Using previously mentioned :focus-visible would be the perfect non-js solution to that problem, as it allows us to style interactive elements based on the input method that a user prefers.

Selector input:focus:not(:focus-visible) should address a user that is navigating, for example, with a mouse. We can revert or disable some rules on demand, if that's the requirement.

And there are even polyfills for this rule (:focus-visible polyfill) - which is nice, since if we want to implement a similar solution on our own - we'll need this logic written down anyway, so we may use a polyfill, that can hopefully be removed once the browsers catch up with the latest CSS features.

This is by far the best solution as:

  • Polyfills are JS based as well, but at some point you can simply remove them
  • Community support - any bugs you encounter could be tackled by anyone who has spare time
  • Minified version of this polyfill is just 3 kilobytes.

So if you're wondering with which solution to go on production - use polyfill. If you want to play around with your pet project, use whatever focus indication solution you wish.

Different methods of styling

Alright, let's discuss various methods of styling and presenting the focus indication to our users.

CSS Custom Properties

First thing that you might want to consider if you're not dealing with legacy browsers are CSS Custom Properties. Amongst many good things they can provide (such as switching themes, less code and faster page loads, being verbose about your code), they also allow you to dynamically change values of some properties that might be handy.

Let's consider an example where we have our custom focus indication styles, so we override the default focus ring with something that works better for us - maybe a thicker width of an outline.

CSS Custom properties
List of files in the snippet editor

With the use of CSS variables we can dynamically change the color of the outline based on whatever card is focused. That's because custom properties are also following cascading model of CSS, so if you set a variable value on a parent - all children will get the same variable value.

Try tabbing in the preview of snippet above and see the results for yourself. Pretty neat, huh!

We should still be careful with such approaches - we want the outline to be helpful as a priority, so we should consider fulfilling the contrast requirements which might also benefit people with colorblindness. Also, making outline a much different color, depending on the element used, may be confusing to users.

Border

An alternative to the outline property can beโ€ฆ the border property. Given that it is not currently in use because our component needs to have some kind of border, we can use it for focus indication.

One of a few downsides for border instead of outline is the fact that it does fall under the Box model definition. So our layouts might start jumping around or simply look weird, especially if we do not use box-sizing: border-box property that automatically calculates the size of padding and border to match element size in that particular direction.

Border approach
List of files in the snippet editor

That can be easily addressed by adding a permanent, transparent border to a card even when it doesn't have focus.

Surely it won't fit every single scenario, but it might work for some:

Border approach - fixing the jumping
List of files in the snippet editor

And if you look at it from another perspecitve - the fact that it's inside the box-model might also be border's upside - as because of that, it won't get cropped by overflow: hidden, which might be needed to achieve more complex designs.

Border approach - simulation of overflow cut
List of files in the snippet editor

Note how overflow: hidden on .card cuts the box-shadow and outline, but keeps border untouched - resulting in a working focus indication! Nice!.

Another one is that border doesn't have to be a rectangle - we can place it on as many sides of a component as we want, while with outline we can only dream.

This can be useful for various cards components or inputs - even though the outline will be less recongizable in my opinion, it is still some kind of focus indication, and is certainly better than nothing.

Border approach - non-rectangle indication
List of files in the snippet editor

It can be very useful if you're dealing with sites that disable outline with the !important clause and you do not have the control over the source code to remove it / fight with a specificity of such selectors, or if you simply don't want a rectangle over your component. It shouldn't matter as long as it is clear to the user.

Background / Color

Another nice alternative to the outline is modifying background/color properties on their :focus. Sometimes it might even be more readable / clear for the user where he/she currently is, as outline might be cropped as discussed earlier - and border may be already taken to fullfill design requirements.

Background and text-color approach
List of files in the snippet editor

I think that background and text-color based focus indication is the most visible for the users, but it's not too subtle.

If you used some kind of CSS preprocessor you could make the color slightly different than cards, lightening it up or darkening it.

Or you could consider using blendmodes, if browser support is good enough for you:

Data on support for the css-backgroundblendmode feature across the major browsers from caniuse.com
Browser support for css-backgroundblendmode provided by caniuse.com

Manipulating text

For links or other text-based interactive elements we can actually change the text-decoration, text-shadow or even font-weight. There's a bunch of possibilities here, and sometimes it can be the only way for us to style the indication, especially with legacy browsers that do not support :focus-within.

While I don't think this is the best solution, it might be that it's all that we can do. I'd still try to go with background / color modification instead as they provide much clearer results in my opinion.

Box shadow

And this is where the fun part starts. To be honest I did not consider box-shadow as something that might be handy for #a11y until recently.

If I were to compare different methods of providing focus indication, I'd call the box-shadow an outline with superpowers. Mainly because it can provide much better results than outline itself, but it also shares one of its constraints.

The biggest downside of box-shadow is that it will also get cropped by overflow: hidden. That's a shame, but it's basically the result of it being outside of the bounding box.

And the rest, well - box-shadow simply rocks. First of all, it will get exactly the same shape as the component on which it is placed, allowing us to have a rounded focus indication, something that is limited in outline case.

Check the syntax for box-shadow property

Box shadow approach
List of files in the snippet editor

Nice, right? And it's getting better. With the border approach we could do some neat tricks, but we needed it to be free for us - if it was taken to represent some part of the design, that's a no go for us on the same element.

This is not the case with box-shadow because it allows us to provide a comma separated list of shadow declarations. Meaning that you can both reflect the design and provide focus indication with the use of it!

Box shadow approach - comma separated values
List of files in the snippet editor

It might not be visible in our case, but in general - there's no conflict. You can have as many shadows as you want!

You can also use this to provide much cleaner focus styles for your components, by stacking the shadows over each other:

Box shadow approach - nicer outline
List of files in the snippet editor

And if you want to provide indication from one side, or a corner it can also be done by manipulating the position of the shadow itself (thought it might not be as pretty as a single direction border).

And perhaps the best part: it won't get disabled by highly specific outline: none !important rules - because, well - it is not technicly an outline ๐Ÿ˜Ž

But still, it is prone to getting cut with overflow: hidden. Whether you'll use that or not depends on you - pick the right technique for your project and your users.

Please do play with your outline

Now that we know a few techniques with which we can present some kind of focus indication to the user, let's face it - the default outline is far from perfect.

It can be hidden by default by something we do not have control, it can be not clear for the user because of our design (default blue rectangle on blue background) and it can be cropped by overflow: hidden rules.

That's why I wanted to repeat the statement concluded in the title of this section: Do play with your outline! Adjust it to your design. Change the colors. Change the method of focus indication if something is not right with outline on its own.

On the web there are plenty of articles telling you to leave it alone, but the reasoning for that statements is not that it should be left as it is - the reasoning is that it's needed for users. So don't be afraid of manipulating or even disabling outline, given that you'll provide the alternative. Just keep in mind other concepts such as progressive enhancement, so users without JS enabled will get what they need too.

Some may say: "But Wojtek, there's a WCAG 2.0 technique no G165 that encourages you to 'Using the default focus indicator for the platform so that high visibility default focus indicators will carry over'".

And I say I know. But as I said, the default outline is often not enough. I stand by the argument that designs should be accessible and readable in the first place, without requiring the user to adjust it to their needs. And default blue-on-blue outline will not be helpful, and various a11y solutions like VoiceOver or GoogleVox provide their own focus indication.

Ofcourse, customisation is very important - but I haven't seen much cases where the OS or browser forcefuly changes the color or size of the outline - if you have experienced such cases, please let me know in the comment. I am always looking to learn more about that.

I just wanted to note that G165 page (read more on G165 technique) explicitly states:

If this is a sufficient technique for a success criterion, failing this test procedure does not necessarily mean that the success criterion has not been satisfied in some other way, only that this technique has not been successfully implemented and can not be used to claim conformance.

So yes, if you want to claim conformance to this success criterion with the usage of G165, leave outline as it is. But I say it's not good for the users, when looking at the bigger picture.

Fancy effects with Flying Focus

As a bonus I wanted to share with you a JS library that provides a nice animation for the outline. Flying focus allows you to create a transition of outline between currently focused elements.

It's not possible with CSS though, so it creates a dummy html node and uses it to simulate browsers outline, animating its position based on the document.activeElement, and its boundingClientRect. The rest is handled with actual transition of CSS, so the performance isn't that bad.

You can see slightly modified version of flying focus on this site - try tabbing around and see how it looks and feels like. For me personally it works great - because I can follow the focus indication dynamically and it feels right. You can try and test it with your users, whether this is a case for them as well.

I've found flying focus on WebAIM (Web Accessibility In Mind), a site which I highly recommend when it comes to ideas, resources and research in regards of #a11y.

Tab order

Tab order - a rule that enforces the focusable elements on a page to placed in predictable and readable matter - is also very important with keyboard navigation. While this topic could be certainly expanded, for now I just wanted to let you know that there's a nice browser extension that will allow you to visually draw a graph where are your focusable elements and in what order they appear.

Accessibility Insights for Web is a browser extension developed and maintained by Microsoft. Besides validation of tab order, it allows you to audit the sites you visit both automatically and manually - providing a nice checklist to go through and check whether site pass the requirement or not.

Outline will only appear on focusable elements

One more thing to note is that some elements, such as custom components will not be focusable with the keyboard by default. So if you create an alternative component of your own, based on div tags you have to recreate every single functionality that native element would provide.

This include proper roles, aria attributes, and the possibility to make the element focusable, if it is an interactive element. To make element focusable, but maintain exactly the same tab order as given in your source code you can add tabindex="0" attribute to your element.

If you don't do that, keyboard users won't be able to use your custom, neat component :(

tabindex can also accept values bigger than 0, but I suggest to never ever do that because it will override the default tab order. On the other hand, tabindex="-1" can be used to make element focusable with JavaScript, but not by user. So if you want to shift a focus to some section after pressing a button, you can add use <section tabindex="-1"> I can be focused by JS now! </section> and it won't pollute the list of interactive elements that user can focus with a Tab.

Cool outlines I found on the web

I wanted to mention few cool outline ideas that are both aesthetic and functional.

One is WebAIMs flying focus - which provides nice, animated effect for outline.

Animated outline with elements background/color shift and underline of a link

The fact that we can easily see to which element the focus shifts and element strongly pops up due to change of background color to yellow-ish and text color to red, makes focused element stand out from the rest. The first time I saw it it really inspired me to go with keyboard indication further.

Another one is Twitter. I've heard people having various #a11y concerns when it comes to twitter, but I like the design of the focus indication for interactive buttons under each tweet, especially in dark mode:

Outline with round focus ring and different icon color for each focusable element to easier distinguish its purpose

There's also a nice aritcle on CSSTricks by Lari Maza on custom focus styles, in which she further explores countless posibilites of styling focus indication.

Why should you disable focus completely?

Why should you disable focus completely? You shouldn't.

Sorry for a clickbait ๐Ÿ™Š. But there's literally zero benefit of you ignoring keyboard users overall.

Even if you develop a WYSIWYG editor or an application to draw online - don't ignore it. I've been working on similar project in my career, and it wasn't the company priority to handle it, especially as it was an internal tool. And I think it's a shame, because even if a tool is supposed to be used with a mouse to draw shapes around, there's a high possibility that it'll have some kind of forms - and those can be navigated by keyboard. Not necessarily by people with disabilities, but by power users.

And if they were ever to release it, well - they would release an inaccessible product. I think it's worth doing your job right at the start, especially when it's not super time consuming in general.

Summary

Here's an final snippet version that you could see from the very beginning.

Desired outcome - final snippet
List of files in the snippet editor

It's also a place for you to play with outline or - websightly as a tool.

Technically this post is more about focus indication in general than regarding outline itself - but regardless, the important part is: create with end user in mind, and try to be as inclusive as possible. And considering keyboard navigation should be a bare minimum of such inclusivity.

Fix the web

The web seems broken. People say that JavaScript is the CO2 of the web, that privacy is no-ones primary goal and that sites are not accessible to widest possible audiences, but rather those privileged by not being disabled or living in a country with better internet access. Developers focus on their convinience by using the trendiest frameworks instead focusing on user bandwidth and bundle size of the website.

Luckily, it's not always the case. Sure, there are big companies who'll sell your data, even without explicitly saying so. Sure, there are products that aren't accessible at all.

But there are tons of people who care about a11y, tons of products that are developed with privacy in mind and tons of ways for us to include wider audiences to stuff we create. I can only wish that more and more of our community will follow this path.

Every little thing that we'll do to fix those issues counts, and certainly - the more the merrier. Don't wait for bussiness' permission, just start doing something in the right direction. If enough people do, it will escalate quickly, as everything on the web.

Thanks for tuning in, and let's #fixtheweb!

Wojciech Poล‚owniak sitting behind a laptop, smiling
Wojtek Poล‚owniak
๐Ÿง๐ŸŽ๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ป๐ŸŽค๐Ÿถ๐Ÿฑ๐Ÿญ๐Ÿ”โœŒ๏ธ๐Ÿณ๏ธโ€๐ŸŒˆ
Have some feedback? Wanna reach out? Contact me at contact@websightly.net!
Back
Forward
Refresh
Permanent outline
New window
Download