惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
LangChain Blog
月光博客
月光博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 【当耐特】
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
博客园_首页
T
Tailwind CSS Blog
P
Proofpoint News Feed
雷峰网
雷峰网
D
Darknet – Hacking Tools, Hacker News & Cyber Security
IT之家
IT之家
V
Vulnerabilities – Threatpost
阮一峰的网络日志
阮一峰的网络日志
C
CERT Recently Published Vulnerability Notes
Attack and Defense Labs
Attack and Defense Labs
S
Schneier on Security
Security Archives - TechRepublic
Security Archives - TechRepublic
L
Lohrmann on Cybersecurity
V
Visual Studio Blog
云风的 BLOG
云风的 BLOG
WordPress大学
WordPress大学
The Register - Security
The Register - Security
N
Netflix TechBlog - Medium
Hugging Face - Blog
Hugging Face - Blog
Project Zero
Project Zero
博客园 - 叶小钗
F
Full Disclosure
大猫的无限游戏
大猫的无限游戏
Latest news
Latest news
S
SegmentFault 最新的问题
C
Cyber Attacks, Cyber Crime and Cyber Security
Google Online Security Blog
Google Online Security Blog
Recorded Future
Recorded Future
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Hacker News - Newest:
Hacker News - Newest: "LLM"
腾讯CDC
L
LINUX DO - 最新话题
Google DeepMind News
Google DeepMind News
P
Privacy International News Feed
I
InfoQ
F
Fortinet All Blogs
Vercel News
Vercel News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Threatpost
T
Tenable Blog
B
Blog RSS Feed

MarcySutton.com RSS Feed

On Joining Khan Academy Developing anti-SLAPP policies for A11y Slack with Harvard Cyberlaw Clinic Focus on What Matters Celebrating One Year of Independence as Modern Sole Design, LLC Evinced is Pushing the Limits of Automated Accessibility Testing Content-visibility and Accessible Semantics Finding accessibility jobs in specialized companies and the mainstream Outsider Leverage and Accessibility Encouraging Open Source Contributions with Docs: a Self-Fulfilling Prophecy Remote Work and Van Life Salary and Career Growth Prototype Testing for Accessible Client-Side Routing On Great Leadership, Gatsby & Girl Develop It The Deal with Developer Advocacy Live Coding Accessibility Chapter Two at Deque 2017, in Music Writing winning abstracts Accessibility is a Civil Right 2016, a Year of Milestones Best of 2016 Music Links vs. Buttons in Modern Web Applications Accessibility and Performance I won an O Web Accessibility Resources This is what a developer looks like. What Wally On writing better captions for images What I’ve Learned Working on a Large Open-Source Framework Speak at your local elementary school. Page Scrolling in Mobile Safari & VoiceOver Accessibility Wins Notes from CSUN 2015 Protractor Accessibility Plugin Riding a bicycle to an accessibility conference 2014: One to Remember AngularJS Material Design & ngAria Summing Up JSConf EU 2014 How I Audit a Website for Accessibility Accessibility and the Shadow DOM: JSConf Australia 2014 CSUN 2014 Conference Recap Accessibility and the Shadow DOM Favorite Music 2013 Girl Develop It Web Accessibility Mobile Web Accessibility with VoiceOver Webstock & NZ 2013 Favorite Music 2012 Target Corporate Site Redesign: Accessible & Responsive Web Development Decibel Festival Recap 2012 Favorite Music 2011 Spiceboard: Wordpress Recipes for iPad POP Clock Favorite Music 2010 CSS + JS + Accessibility Christmas JS1k Zend Framework. NACCC Urban Type Sutton RV Simplexml in php 5 AS3 Load Workflow AS3 Mouse Events Holiday 2009 Why Outlook Sucks
Button Focus Hell
2015-04-25 · via MarcySutton.com RSS Feed

April 25, 2015

Note: This post (in part) led to a web standards proposal by Brian Kardell and Alice Boxhall called CSS Input Modality and later, the proposed CSS pseudo class :focus-visible. The problem described below can be addressed with the :focus-visible polyfill, or alternatively, the What Input library by Jeremy Fields. They are really helpful tools!

When I started working on Angular Material, there was a lot of low-hanging fruit with regards to things I could fix for accessibility. One of my first Github issues captured the state of mdButton, a fancy Material Design button component using <canvas> to create an interactive touch ripple:

<material-button class="material-button-raised">
  <canvas class="material-ink-ripple" style="top:0px; left:0px"></canvas>
  <button class="material-button-inner"></button>
  <span class="ng-scope">Button</span>
</material-button>

Angular Material Button, August 2014

A few things have changed with this UI component since then, including the tag name, now md-button for brevity; CSS class prefixes; and, the ripple approach, which instead of canvas nests a child div with CSS animations. My first accessibility pass put the text content into the native child button. Buttons provide native semantics and are focusable by default, so text content can be read aloud in a screen reader. (This exercise showed me that UI framework developers really need accessibility skills.)

Angular Material has come a long way since then. We’ve added a bunch of features and closed 1500+ Github issues! These days, you can instantiate mdButton by including the custom element in a project with the required dependencies.

<md-button>Button</md-button>

The default button uses Angular to render markup that looks like this:

<button class="md-button md-default-theme" ng-transclude="">
    <span class="ng-binding ng-scope">Button</span>
    <div class="md-ripple-container"></div>
</button>

A persistent “bug”

Throughout many changes to the framework, there was one issue that came up again and again–sometimes it had a different title, but the underlying problem always smelled the same: “button stays highlighted after clicking.” It came up for buttons, checkboxes, sliders and radio buttons, and people thought it was a mistake in our CSS. Knowing the root cause was the browser and without an obvious solution, I put some feelers out on Twitter and got a telling response from Patrick Lauke:

“Welcome to a world of pain.”

Google Chrome, in which (admittedly) most of our users test, clicking with the mouse on a native or custom button does what it should: it sets focus on the element, so hitting TAB will move on to the next focusable element in the DOM. What it shouldn’t do, however, is persist the focus style, be it the browser-default focus ring or a custom background color.

Animated GIF showing persistent focus on Material Flat Button

Firefox actually gets it right for native buttons: clicking sets focus, but not the visible focus style. Hitting TAB will show the focus style on the next button, since it was an intentional keyboard interaction. Custom <div> buttons, on the other hand, suffer the same fate as Chrome. (There’s a reason why everyone says use native buttons: they’re more predictable!)

I could have spent an infinite amount of time checking this in other browsers, but it wouldn’t change the fact that Chrome has a perceived bug as far as our users are concerned. A quick search in the Angular Material Github repo turned up 7 related issues, and without a fix duplicates would keep rolling in. So I started experimenting with solutions, knowing all of them would be some kind of hack.

To see how it was not handled at this point, check out my Codepen focus sandbox using Angular Material 0.8.3.

I imagined a fix would include detecting whether the user had in fact clicked with the mouse, setting a flag with JavaScript, and only showing keyboard focus if this flag was not present. Obviously, requiring JavaScript to set the correct focus style is…not good. But considering this was for an Angular UI framework, it seemed like a moot point.

I will say that Material Design needs better visual contrast, especially for interactions. It has become very apparent as I’ve included components in conference talk slides and within my website’s design. But that is a separate issue.

Finding Solutions

In this post, we’ll focus on the fix that went into Angular Material, which uses a single button element. An alternative version required managing two elements (one offscreen) and using ARIA, which was a completely different code smell.

After some experimentation with mdButton, I landed on a solution with JavaScript looking like this:

// restrict focus styles to the keyboard
    scope.mouseActive = false;
    element.on('mousedown', function() {
        scope.mouseActive = true;
        $timeout(function(){
          scope.mouseActive = false;
        }, 100);
      })
      .on('focus', function() {
        if(scope.mouseActive === false) { element.addClass('md-focused'); }
      })
      .on('blur', function() { element.removeClass('md-focused'); });

Angular Material Button Directive Source

In English: initialize a mouseActive “flag” variable to false. Bind a mousedown listener that sets the flag to true when a user actually clicks with the mouse, resetting it to false after 100 milliseconds. Bind a focus listener that checks whether mouseActive is false and adds an .md-focused CSS class. A blur event removes the class. This way, the focus style will only be added if the user is truly using the keyboard.

Not exactly related but sort-of, in version 0.9.0 I removed CSS preventing the browser’s default focus style on all elements (a blue ring in Chrome). As a UI framework, being this heavy-handed with your CSS is bad for people who use it. You are essentially setting them up to fail with keyboard accessibility, since there is no easy way to add it back (and many won’t). In my case, creating sandboxes on Codepen comparing native elements to Angular Material components necessitated this change. Instead, we override the default focus style in each specific component. It felt good to do the right thing as a framework developer, despite a Github issue questioning the move.

UI heaven…for now

A similar, but slightly more complicated, approach was added to checkboxes and radio buttons. They are more complicated because there are use cases where you might click with the mouse to select something and then use the SPACE bar to activate or deactivate the control. But for buttons, this approach seems to work fine for now. Ideally, Chrome will just fix the bug on the browser side and then we can remove these hacks and await the next version of custom-UI-hell.

Note: relying on NO focus styles to “fix” the persistent focus problem does not count as a solution.

Here is an updated Codepen using Angular Material 0.9.0-rc2 (final release coming soon):