Detecting users don't require visible focus rings


#1

It is useful that CSS selectors have the ability to detect users don’t require visible focus rings.

:focus {
outline: 5px solid;
}

:focus:-visual-focus-ring-is-not-required {
outline:none;
}

Motivation

Native applications on the several platforms don’t draw focus rings until the users press the keyboard or use voice control software and etc. So users who use pointing devices mainly rarely see the focus rings. On the mobile, especially, majority of users use the touch screens and rarely see the focus rings.

On the other hand, web pages draw focus rings more frequently as major browsers move the focus to buttons and links by clicking. Recently, Google Chrome changed its behavior: moving the focus by clicking on a link (issue 388666).

So uses who use pointing devices mainly see the focus rings by just tapping the screen or so. Though there are some workarounds to hide unintended focus rings (described below), it is useful the CSS selectors have the ability to detect users don’t require visible focus rings.

Proposal

Adding -visual-focus-ring-is-not-required pseudo class (this name is tentative) to the CSS Selectors.

:focus {
outline: 5px solid;
}

:focus:-visual-focus-ring-is-not-required {
outline:none;
}

:-visual-focus-ring-is-not-required matches

  • Users haven’t use keyboard or related input devices for navigation and
  • Accessibility services are not working and
  • User Agent preferences allow :-visual-focus-ring-is-not-required in content

Current workarounds

outline:none

Some websites use outline:none . This harms the keyboard or other input devices users. Don’t use it.

ChromeOS

ChromeOS uses the JavaScript to watch users keyboard input and maange the focus ring visibility. ChromeOS draw focus ring if users have pressed the keyboard and encourage developers use JavaScript. For example, in issue 305356

Web authors need to use something like [1] to distinguish mouse-focus and keyboard-focus.

I think it is too much that every website or applications use JavaScript to hide focus ring.

Mozilla

Firefox OS has disabled outlines by default (bug 1132728) as Firefox OS doesn’t support keyboard.

We can’t do this as other platforms support keyboard.

Mozilla has :-moz-focusring pseudo-class that matches an element “if the element is currently focused AND the user-agent has focus ring drawing enabled” (bug 418521).

-moz-focusring is intend to be used in the User Agent style sheets and the approach of -moz-focusring cannot apply the web content. Because it will break backward compatibility.

Let’s consider following example:

:focus {
/* unsupported browsers don't render the focus ring! */
outline:none;
}

:-moz-focusring {
/* only supported browsers render the focus ring */
outline:5px solid;
}

Add meta on 'focus' event
#2

This post is follow up post that includes links to related pages (I removed the links from the previous post as I can include 5 links in a post).


#3

No opinion yet on whether this is useful or not, but handling “are you capable of drawing focus rings” is best done through a MQ, not a specialized pseudo-class variant.


#4

It seems to me this would cover it:

@media (pointer: coarse) and (hover: none) {
}

@Takenspc that doesn’t check if “accessibility services are not working” but I think this case covers it, what do you think?

Certainly I have never heard the feature called focus rings.


#5

No, this is about keyboard access, not pointers. The “focus ring” is the default dotted outline or gray/blue glow around the currently focused element. It’s necessary for keyboard navigation, so you can tell where you are in the page.


#6

Yeah I got that, I should have been more clear the code I think should have been:

:focus {
  outline: 5px solid;
}

@media (pointer: coarse) and (hover: none) {
  :focus {
    outline: none;
  }
}

#7

I know this is a discussion about a standard CSS solution, but in terms of a practical JavaScript-based approach that can be used today, how about:

  1. During page initialization, dynamically inject a :focus { outline:none } style sheet and bind a “keydown” handler.
  2. Using the handler, if a TAB press is detected, revert the styles injected in step 1.

Please let me know if this is a bad approach :slight_smile: . I’m curious if this sort of TAB detection is performed in the wild to conditionally toggle focus rings, or what other techniques are used.


#8

As a front-end developer who does websites for many clients, this is definitely a problem that comes up on most projects, and something that I need a good solution for.

Currently our team best practices mandate that we duplicate most :hover styles with :focus, for instance:

.CoolBtn {
  color: white;
  background-color: #555;
}
.CoolBtn:hover, .CoolBtn:focus {
  background-color: green;
  outline: none;
}

When we have visible focus styles, we may also hide the default outline, for a cleaner look.

Issues arise for links-that-behave-as-button (e.g. progressively enhanced links) and <button>, and other focusable elements that can be clicked or pushed and that will retain the focus after they’re clicked:

  1. A button that was clicked to perform an action can stay in a visual state (the one used for both :hover and :focus) that suggests it is “pressed” or “activated”. That can be puzzling for end users.
  2. Other leftover :focus styles do not pose such usability challenges, but can still be visually jarring, and clients often ask that they be removed, which leads to the tentation of using outline:none all over the place and never designing focus styles for keyboard navigation (I’ve NEVER had a client who cared about it, unless they specifically had to pass an accessibility certification).

Current solutions include:

  • Implementing designs with visible focus styles, and only removing some of them in CSS when the client actually complains about it.
  • Using JS to move the focus from the clicked button (or buttonified link) to another element in the page, when the button is clicked. (For instance moving the focus to the form element for a navbar button that reveals a search form.)
  • Using JS to add a class to an element when it’s clicked, and remove it when it loses focus.

My current code for that last solution (not including the CSS part) is, with jQuery:

$('.CoolBtn').on('click mousedown focusout', function(event){
    var btn = $(event.currentTarget);
    var t = event.type;
    if (['mousedown', 'click'].indexOf(t) + 1) {
        btn.addClass('noFocus');
    } else if (t === 'focusout') {
        btn.removeClass('noFocus');
    }
});

(Hasn’t been tested enough on touch devices, for the record.)

Possible native solutions I’d like to see:

  1. Changing or clarifying specs so that “activation” of an UI element does not give it :focus (only :active styles applied, no leftover :focus).
  2. Or adding an alternative to :focus that describes focus gained through keyboard navigation or use of assistive technology.
  3. Or adding a pseudoclass with a behavior similar to my JS solution above (though it might need to be more specific), which could then be used independently or in selectors such as :focus:not(:that-kind-of-focus) {…}.

#9

It is.

  • Use of keyboard navigation doesn’t preclude other kinds of interaction. (Users might use the Tab key to navigate locally, for instance between form controls, then keep using mouse clicks, taps, etc.)
  • Not all “focusing” navigation will use the Tab key anyway. Opera used to have a mode for 2-dimensional keyboard navigation using the arrow keys, and other keys dedicated to one-dimensional keyboard nav (like Q and W or something like that).

A similarly bad solution that is sometimes floated around is adding a class on the body the first time a mousemove is detected.


#10

I’ve been using this only for a while now, but it seems to work fine across all browsers:

:focus
{
    /* normalized default styles for all browsers */
    outline: currentColor dotted thin;
    /* on webkit and blink use focus ring;
       style of “auto” hides it, when using mouse or touch */
    outline: -webkit-focus-ring-color auto 5px;
    /*
    if one don't like webkit's ring, use this instead:
    @supports (outline-color: -webkit-focus-ring-color)
    {
        :focus { outline-style: auto; }
    }
    */
}
/* don't use outline on mozilla browsers, if it's not needed */
:focus:not(:-moz-focusring)
{
    outline: none;
}

… but I’d rather see something like :-visual-focus-ring-is-not-required standarized, instead of opposite :-moz-focusring as later encourages bad practise (giving :focus no outline and only adding it for browsers that support the selector).