Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Action button doesn't pop back up after a click #2500

Open
hadley opened this issue Jun 15, 2019 · 10 comments
Open

Action button doesn't pop back up after a click #2500

hadley opened this issue Jun 15, 2019 · 10 comments
Labels
Consult Team Requires consulting the Shiny team Type: Accessibility

Comments

@hadley
Copy link
Member

hadley commented Jun 15, 2019

After running the example in shiny::actionButton and clicking on the action button, I see:

image

This looks like the button is still pressed down; it should pop back up on mouse up.

@bklingen
Copy link

I agree. Currently, the button reverts to the original state only if one clicks somewhere outside the button. I often want users to repeatedly press the button to trigger some action each time the button is pressed. For that, the current behavior is not very intuitive. (The best I can do is to include a "refresh" icon to signal users the button can be pressed again.) Ideally, the button would flash (change colors, change border color, etc) for a brief moment when pressed, but then revertsback to the original state.

@nteetor
Copy link

nteetor commented Jun 16, 2019

Do you see this behaviour across browsers? I notice Chrome and the RStudio viewer handle the button focus state differently than FireFox, which has no such state as of now. Perhaps the trick is to hide the added focus state for Chrome and Chrome-like browsers.

@bklingen
Copy link

Yes, I see this behavior in RStudio viewer, Chrome, Firefox and IE. The button only reverts back to its initial configuration (and changing color on hovering) when first clicking somewhere outside it.

(The behavior also occurs with the nice actionBttn provided by the shinyWidgets library. Once pressed, it doens't revert back to the original appearance/color unless one clicks somewhere outside the button.)

@nteetor

This comment has been minimized.

@trestletech
Copy link
Contributor

trestletech commented Jun 20, 2019

I'm testing on a Mac with:

library(shiny)

ui <- fluidPage(
  actionButton("go", "Go"),
  numericInput("n", "n", 50),
  plotOutput("plot")
)

server <- function(input, output) {

  randomVals <- eventReactive(input$go, {
    runif(input$n)
  })

  output$plot <- renderPlot({
    hist(randomVals())
  })
}

shinyApp(ui, server)

I can reproduce the bad behavior in:

  • Chrome (75.0.3770.100)
  • RStudio IDE Viewer (1.2.1226)

I do not see this behavior on:

  • Firefox (67.0.2)
  • Safari (12.1.1)

@trestletech
Copy link
Contributor

trestletech commented Jun 20, 2019

It looks like the browsers are behaving this way intentionally for accessibility reasons. Relevant conversation: foundation/foundation-sites#6105

The proposal seemed to distill to:

whenever a button is focused, the background does not change, regardless of it was focused from keyboard or mouse? So that would mean the only visible indicator you would have that a button is focused, is the blue outline if you used the keyboard to select it?

i.e. preserve the focus ring when navigating by keyboard, but omit it when clicked by mouse. And don't style the button's background color differently on focus. But we will need to force an outline since not all browsers offer it by default.

Focus ring comes from the following CSS in bootstrap.

.btn:focus,
.btn:active:focus,
.btn.active:focus,
.btn.focus,
.btn:active.focus,
.btn.active.focus {
  outline: 5px auto -webkit-focus-ring-color;
  outline-offset: -2px;
}

@trestletech
Copy link
Contributor

trestletech commented Jun 20, 2019

Option 0

No-op, preserve default behavior and let users opt-out themselves. We could do one step better and build our own opt-out that they can manage via R.

Option 1

The referenced what-input package is an interesting option. Popular npm module with no dependencies, MIT, 4.5kB minified. Enables you to define CSS rules like:

/*
 * only suppress the focus ring once what-input has successfully started
 */

/* suppress focus ring on form controls for mouse users */
[data-whatintent='mouse'] *:focus {
  outline: none;
}

but still will likely require us spelunking through the bootstrap styles.

Option 2

A lighter-weight solution would just be to blur on-click which, I think, would still preserve accessibility. https://stackoverflow.com/a/25877412/1133019, but might show a flicker of the outline.

e.g.

htmltools::HTML("<script>$(function(){ $('.btn').mouseup(function() { this.blur(); console.log('hi'); }) });</script>"),

seems to preserve the right behavior for keyboard and mostly solves the mouse issue. It does add a flicker of the outline for browsers that add the outline, though.

focus

This also brings it in line with Firefox's default behavior -- show highlight when keyboarding, suppress when mousing. It seems like that's what users expect (at least on this thread).

focus-ff-vs-chrome

Option 3

There's also a focus-visible draft which aims to solve this problem but it's only supported by Firefox, at the moment: https://caniuse.com/#search=focus-visible. There's a polyfill available but it's under a weird license that we'd need to explore more. It's also a dependency-less, small JS file. This route would still require us to specify our own additional CSS rules that leverage this new class to remove the ring. A nice article on the topic: https://fvsch.com/styling-buttons/

Thoughts

For option 1 or 3, we'd need to consider users who are using custom bootstrap themes. I think we could come up with a universal rule that would undo the outline ring but undoing custom background colors for focused elements might not be feasible.

@tsmuse
Copy link

tsmuse commented Jun 20, 2019

In general hiding the focus outline ring is a dark pattern, the system should always be transparent about where focus is. The issue here seems to be Chromium fires both the click/touch events and the focus events on buttons, unlike Webkit and Gecko. Here are some things to keep in mind for any fixes:

The ideal way to fix it would be to restyle the focus ring so it doesn't get mistaken for the active state of the button. This leaves the tab-focus systems for each browser intact and consistent (at least across sites that aren't trying to hack the focus ring). While this could be overridden with custom CSS, that's true for pretty much anything we do.

Unfocusing the button on mouseup for buttons that are not toggles will make the UI visually consistent across the three major rendering engines, but it will make Chrome's non-standard tab-focus flow less usable and accessible. The way focus flow works in Chrome is that clicking focuses the element and then tabbing from there picks up from where the user clicked. (In the other browsers the tab-focus is independent of the mouse clicks, so clicking doesn't interrupt where the focus currently is). Blurring the button on mouseup doesn't effect where the user is in the tab order in any browser, so Chrome users will still be changing their place in the tab-order but now they won't have an indication of where they are until they hit tab next.

@trestletech trestletech added the Consult Team Requires consulting the Shiny team label Jun 25, 2019
@BajczA475
Copy link

I'm only just now finding this thread, but I think I've run into this problem as well, and my (very dumb) workaround was to use shinyjs to disable and re-enable the button back to back upon press inside the button's observer, which seemed to reset its visual aesthetics for me. I'm not sure if this suggests a possible solution, but I thought I'd mention it!

@gadenbuie
Copy link
Member

At its core, this issue is about the style of the button when the button is focused in the browser. This is handled by Bootstrap in different ways across versions:

Bootstrap Normal Focused
3 image image
4 image image
5 image image

In general, it's least obvious what's going on with Bootstrap 3 and we'd recommend using Bootstrap 4 or 5 if possible by adding theme = bslib::bs_theme(version = 5) to your page functions or using a page_*() function from bslib directly.

I do not recommend trying to change the :focus or :active styles, or using JavaScript or shinyjs to reset the button state or otherwise move the focus away from the button. Doing so reduces your users' ability to use your app with the keyboard or assistive technologies. It's understandable that you'd want to improve your app's visual appearance, but Bootstrap, especially in the most recent versions, provides consistent and careful focus indicators that you can rely on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Consult Team Requires consulting the Shiny team Type: Accessibility
Projects
None yet
Development

No branches or pull requests

9 participants