cursor: pointer and user-select: none
cursor: pointer
I was poking around Adobe’s design library, Spectrum, and noticed it doesn’t show the cursor as a pointer
, instead it uses the default
cursor for clickable elements.
Searching a bit led me to the GitHub PR where they changed the cursor style from pointer
to initial
.
Why the change? Gist is native apps use cursor: default
for buttons and interactive elements and they only use cursor: pointer
for links, which is laid out in the human interface guidelines from Apple and Microsoft.
Additionally, the spec for the cursor: pointer
says it should be used when, “The cursor is a pointer that indicates a link.”
So cursor: default
brings the web close to a native experience.
I decided to look around for some “app like” experiences in web form and see how they handle their cursor styling:
name | cursor |
---|---|
Discord | pointer |
Dropbox | pointer |
Excalidraw | pointer |
Feedly | pointer |
Figma | default |
Google Docs | pointer |
iCloud | pointer |
iCloud Pages | default |
Linear | pointer |
music.apple.com | pointer |
Trello | pointer |
Also I didn’t check, but I assume Adobe’s online stuff uses cursor: default
since their design library does.
selection
In addition to using cursor: default
, native experiences don’t let you select things by default, while the web does.
So if you navigate to your bog standard page and hit ctrl + a, then you’ll get a bunch of random boxes of content selected – not a native experience.
So how can we avoid this?
One option is to capture the ctrl + a with an event handler, but this still allows for selection of random elements via the cursor / touch input.
A more robust way to prevent selection is to use our friend user-select: none
high up in the tree and then when we actually want a user to be able to select something, we explicitly allow it with a user-select: text
Which sites use user-select: none
?
name | uses user-select: none ? |
---|---|
Discord | yes |
Dropbox | no |
Excalidraw | mostly |
Feedly | sometimes (sidebar nav) |
Figma | mostly |
Google Docs | sometimes (toolbars/buttons) |
iCloud | no |
iCloud Pages | yes |
Linear | yes |
music.apple.com | yes |
Trello | yes |
Conclusion
I think cursor: default
is a good default, but even though it goes against the guidelines / spec, for clickable items I like cursor: pointer
.
With selection, I think user-select: none
is a good default, but with great power comes great responsibility, there are things that should be selectable!
Update 2022-11-27: I ended up adding these settings to Recipeyak. Took a few PRs to get it all setup:
Addendum (2022-11-30): :hover
:hover
is another edge case to consider when trying to get a more native feel. With browsers, :hover
on mobile can get “stuck” enabled when using touch.
This can be easily fixed by wrapping :hover
declarations in a @media (hover: hover) { }
.
Defensive CSS has a good post documenting this behavior.
Also if you’re using Tailwind you can enable hoverOnlyWhenSupported
in the config to automatically add the media queries to hover styles.