-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
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
[Button] Implement inclusive disabled state #27719
Conversation
@material-ui/core: parsed: +0.12% , gzip: +0.11% |
e7d255a
to
b89ea22
Compare
For the prior art, I think that we can also mention:
|
Happy to see this. The only thing I'm wondering is the name. Would |
Me too 😃
Yeah, the name was just a working title for me. "accessibleDisabled" sounded a bit weird at first. The "enableAccessibleDisabled" approach sounds like it would also require |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Is it related to https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_disabled_controls?
- I have tried the
inclusiveDisabled
prop on a Button but I couldn't Tab on it: https://codesandbox.io/s/textbuttons-material-demo-forked-hq1m4?file=/demo.js. Is it expected?
[`&.${buttonClasses.inclusiveDisabled}`]: { | ||
cursor: 'not-allowed', | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would propose we use the default cursor when disabled because we use the pointer cursor when enabled. It would already differentiate the two cases.
Looking a bit closer on Windows:
- On a regular
<button>
, the cursor is identical https://codepen.io/oliviertassinari/pen/oNWRKKq
disabled.native.windows.mp4
- The not-allowed cursor looks unfriendly
disabled.windows.mp4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would propose we use the default cursor when disabled because we use the pointer cursor when enabled
The pointer cursor is already wrong.
With your argumentation we could also use the drag cursor. You're missing why we should use the default curso.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're missing why we should use the default cursor
The argumens for why was 1. default cursor is how the native button behave, it seems enough 2. not-allowed looks "unfriendly".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- default cursor is how the native button behave
So you agree that we should also use cursor: default
when the Button is not disabled because it's how the native button behaves?
- not-allowed looks "unfriendly".
Don't agree there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eps1lon I recall a similar discussion in #17871. I personally think that the expected behavior is button with cursor: pointer
on the web, and cursor: default
on the native UIs.
Why? It's how the apps I use on my OS, e.g. macOS Finder, and the websites e.g. GitHub I use on a day to day have trained my brain over the years. I would find something else confusing.
Is it the right behavior? Windows explains why on a native UI cursor: default
should be used.
Win32 / Design
To avoid confusion, it is imperative not to use the hand pointer for other purposes
We see the same in https://developer.apple.com/design/human-interface-guidelines/macos/user-interaction/mouse-and-trackpad/ ("The content beneath the pointer is a URL link to a webpage, document, or other item.") and https://www.w3.org/TR/css-ui-3/#cursor ("The cursor is a pointer that indicates a link."). The community, at large, seems to have decided to override these guidelines. At the end of the day, I think that it's ultimately a convention to create predictability. So I think that going against the trend on the web would be wrong. It would be better to update these convention guidelines: w3c/csswg-drafts#1936. Which they did: w3c/csswg-drafts@272dd50.
Back to argument 1. I think that it could work because we would use two different cursors between enabled and disabled.
Don't agree there.
For 2. it's fair, I think that it's a matter of feeling, no objective arguments to find :). I was personally influenced by twbs/bootstrap#22222 (comment).
Since this is a UI/UX problem, in the end, I think that we can involve @danilo-leal, to bring a new perspective, as a designer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually don't have a new perspective to offer 😅 I read the CSS tricks article and learned from it, it makes a lot of sense. I also don't have any problem with the not-allowed cursor... Really liked how the experience turned out to be on this one. And happier that is making it more accessible :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a hot topic very polarized. From one side we have the "web guidelines" (from Microsoft, w3c, apple). On the other side, we have the actual user expectations.
Based on Apple website, expectations seem to be more important than "guidelines". For example, the bag button, uses the pointer instead of the default one.
My thoughts are better summarised by this tweet from David K:
So cursor: pointer is for navigation and not for performing some action.
You know who knows that rule?
Approximately 3% of web developers and 0.0000001% of users.
Matching user expectations is what makes me adding a pointer cursor to normal buttons.
Then we have another point: aesthetic tastes. not-allowed
cursor looks different from OS to OS macOS example. This might lead some people to override it in their apps because it's not aesthetically pleasing (a.k.a. ugly).
We already have the pointer cursor to distinguish clickable from non-clickable elements.
With all of that said... Honestly, I don't have any preference, both sides have pros/cons. But if I had to take a side probably I'd be inclined towards using cursor: default
on disabled buttons to keep the consistency with the current disabled
prop at MUI that does not change the cursor either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Matching user expectations is what makes me adding a pointer cursor to normal buttons.
I think web developers tunnel vision too much on the user interface they work on. Ordinary people don't know where the OS ends and the web begins. There's only a single "computer". The OS does not use pointer
for buttons . There may be exceptions but the majority of clickable surfaces in macOS, Ubuntu (gnome) and Windows 10 do not use the pointer
cursor.
Then we have another point: aesthetic tastes. not-allowed cursor looks different from OS to OS macOS example. This might lead some people to override it in their apps because it's not aesthetically pleasing (a.k.a. ugly).
Doesn't that go against "matching expectations" i.e. "not-allowed
cursor looks different from OS to OS macOS example." is exactly the point. People will recognize "not allowed" semantics since that's what they're used to from their computer.
We already have the pointer cursor to distinguish clickable from non-clickable elements.
Not sure how this is relevant here. But it's controversial as well.
But if I had to take a side probably I'd be inclined towards using cursor: default on disabled buttons to keep the consistency with the current disabled prop at MUI that does not change the cursor either.
This is simply due to our current implementation. You currently cannot change the cursor for <Button disabled />
since it's imlemented with pointer-events: none
. So the argument for historical reasons doesn't apply here.
Expected but not intended. |
@eps1lon
Edit: I just saw the TODO |
Sorry, should have mentioned that. The PR is in a weird spot since I do want some feedback but don't intend to actively work on it right now. It can be pushed to 5.1 or even be included in a set of changes focusing on a11y (a11y statement etc). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, Sandrina here! 👋
I'm glad my article about disabled button motivated you folks to improve Material UI 🥰 Thank you!
@@ -6,6 +6,7 @@ import { Icon } from './Icon'; | |||
|
|||
interface Props extends MuiIconButtonProps { | |||
disabled: boolean; | |||
inclusiveDisabled: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @mbrookes (#27719 (comment)) about the name inclusiveDisable
, it's a little strange.
What do you think of disabledClickOnly
? Beneftis:
- autocomplete will work when start typing
dis...
- more people will notice the new prop. - It reflects the purpose: only the click is removed - all the other interactions are still possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
autocomplete will work when start typing dis... - more people will notice the new prop.
That is a great point.
It reflects the purpose: only the click is removed - all the other interactions are still possible.
Yeah, I'm a bit split on this part. Because I generally don't want to name props after "how" they do something but rather what they're for. For example, I rather name a prop "error" instead of "red" even though "red" is what's actually happening.
Though I lean towards optimizing for having the prop right next to disabled
when it comes to sorting. And "inclusive" is almost as bad as the generic "accessible" tag line: "inclusive" for whom?
@@ -330,6 +334,7 @@ const Button = React.forwardRef(function Button(inProps, ref) { | |||
disabled={disabled} | |||
focusRipple={!disableFocusRipple} | |||
focusVisibleClassName={clsx(classes.focusVisible, focusVisibleClassName)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[@espipj] Do we need focus-visible styles when inclusiveDisabled?
Inclusive disabled buttons still allow focus with the keyboard, therefore it needs a focus indicator. Otherwise, people might not know what's the currently focused element.
This prop can be used in more scenarios besides tooltips elements. That means we can't assume that there will always exist a visual clue from a surrounding element (eg the tooltip). For that reason, yes, add the focus indicator, even if it's redundant in some cases. Redundant is better than none. Does this make sense to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that's exactly the argument I would make. Though I'm always grappling with the concept of "cursor" and how it relates to mouse and keyboard inputs. We remove the hover styles for mouse cursor as well which is to me the same as focus styles for the "keyboard cursor".
But I am still leaning towards having different visuals for hover and focus styles because the mouse cursor is inherently visible. The keyboard cursor has no inherent visibility so it makes sense to always display them. Removing focus-visible styles is like hiding the mouse cursor once you hover over an element.
I'm biased, obviously, but at my place of work, we made it the default behavior. Until now we didn't find a real-scenario expectation yet, although we thought about it. For example |
Yeah that's what I'm dreadding. The size of our audience requires that we communicate the difference. And people have a certain expectation about So while I'm writing this I just realzied that form integration isn't solved. For example, |
@eps1lon I agree that shipping this can have side effects. Taking into account Material UI is widely used, I'd make it optional as a first version, promote it, and see how the community reacts.
Yeah, that's a downside I mentioned in the article, we need JS to prevent that. This also makes me realize that whatever approach we take here, it should gracefully fall back in non-JS browsers, where the native |
I don't think React supports that. You'd have to render
Note that this isn't trivial at all since form submits via textbox are submitted from the corresponding buttons. This gets especially tricky since form submission via textbox is a user agent thing not specified behavior. So we need to do more research in that area and setup tests to monitor user agent behavior (since that can change at any point). In short: It's not as simple as putting |
True. One more reason to add this as an enhanced/optional approach rather than being the new default behavior. That way is up to the developers to use that approach whenever they see the fit for it. And that also avoids breaking changes. |
Sure, but we can't just add the option and expect people to use it properly. The proper usage needs to be part of the documentation and cases where it fails also need to be prominent. That's why I'm saying this needs a bit more work to be releaseable in a stable release. |
@eps1lon I understand that and wasn't expecting anything different than a comprehensive documentation about this new prop. :) |
9e037b3
to
0f5a0c6
Compare
Couple of thoughts after reading it all:
I was trying to think of a way if it was possible to add this as a default @eps1lon you mentioned that using current approach would break forms because submit button would capture enter. This could also be "tricked" by conditionally changing button's type, i.e. As for all the other things mentioned in the table:
So I'm wondering what else could potentially break there? 🤔 Alternative suggestions: what if instead of adding new prop and instead of doing this as a default
It very much feels to me as developer that this is the default behaviour I'd expect disabled buttons to have |
I remember some subtle bugs with HTML forms and event order when |
This PR is inactive since a long time. I suppose we can close it. |
Adds a prop that disables "click interactions" for buttons but enables "cursor interactions" (hover for mouse, focus for keyboard).
Preview: https://deploy-preview-27719--material-ui.netlify.app/guides/right-to-left/#opting-out-of-rtl-transformation
This is largely an implementation of https://css-tricks.com/making-disabled-buttons-more-inclusive/.
TL;DR: Disabled buttons are mostly inaccessible to keyboard users and oftentimes don't tell why they're disabled. We can show additional "why" information for disabled buttons on hover/focus.
Use case for us
Prior art
#26412
#23101
Implementation
Adds a new
inclusiveDisabled
prop. We could hijackaria-disabled
but this would be a breaking change. A separate prop gives us the opportunity to experiment with it (potentially "unstable_" prefix?).Future
For v6 we can debate whether this should be the default behavior for buttons (tooltips for disabled buttons are a common footgun).
TODO
inclusiveDisabled
?It might be disorienting to not display the cursor. But it's inconsistent with mouse-cursor i.e. hover where we don't have additional styles when hovered.Yes we do. Mouse cursor styles are removed but the cursor itself is still visible. Removing focus-visible styles removes the keyboard cursor entirely though.