[bug]: Cursor pointer not working when hovering on button in Tailwind v4
Describe the bug
Description
When I hover over a button, the cursor does not change to a pointer as expected. This issue occurs in my Next.js 15 project using ShadCN v4. I also checked the https://v4.shadcn.com/#button, and the same issue is present there. The cursor does not display as a pointer when hovering over buttons.
Affected component/components
Button
How to reproduce
- Set up a Next.js 15 project.
- Install and configure ShadCN v4.
- Add a button component using ShadCN's button implementation.
- Hover over the button.
- Observe that the cursor does not change to a pointer.
Codesandbox/StackBlitz link
https://codesandbox.io/p/devbox/8jqnnq
Logs
System Info
1. Framework: Next.js 15
2. ShadCn: v4
3. Browser: Tested on Chrome, Edge
4. OS: Windows 11
Before submitting
- [x] I've made research efforts and searched the documentation
- [x] I've searched for existing issues
I would like to know more about this decision process, is it no longer cool to have cursor: pointer on button nowadays, or what? Am I missing something here? Does this conform to some HTML spec?
If anyone has more information on this please let me know.
https://ux.stackexchange.com/questions/105024/why-dont-button-html-elements-have-a-css-cursor-pointer-by-default
They just removed cursor pointer of button by default in Tailwind v4, so I had to add custom CSS to globals.css
@layer base {
button, [role="button"] {
cursor: pointer;
}
}
@aow3xm thanks for sharing that snippet. I would add this to ensure you don't get pointers on disabled buttons:
@layer base {
button:not([disabled]),
[role="button"]:not([disabled]) {
cursor: pointer;
}
}
This is a really strange behavior. I expect my users to see they are hovering a button with their cursor changing. It's the most common practice, and even when browsing shadcn documentation, buttons are still showing this cursor pointer :(
https://ux.stackexchange.com/questions/105024/why-dont-button-html-elements-have-a-css-cursor-pointer-by-default
Thank's for this, very instructive.
However I truly think that having an option to keep the old behavior would be really nice since most of huge website are using pointer cursor on buttons (github, facebook, microsoft, stackoverflow, ....).
Try explaining this to 99% of product managers, QA, any non techs or just run with the above mentioned hack with targeting all buttons. I wish this decision was up for community discussion because I do consider this to be a breaking change.
It'd be great if we call this out on the component examples or have them follow the default behavior. I was wondering why my local install was not matching the examples/docs until I stumbled on this
fucking retards!
I don't understand, this should be reopen
Strange behavior.
@aow3xm thanks for sharing that snippet. I would add this to ensure you don't get pointers on disabled buttons:
@layer base { button:not([disabled]), [role="button"]:not([disabled]) { cursor: pointer; } }
Thanks for the snippet.
Button with Link variant also has a default pointer. That one could at least have a pointer cursor.
This threw me for a loop lol. I wonder why tailwind decided to make this default. Very strange. It also makes is more difficult a user to see when a button is disabled, notably, with the secondary variant on the shadcn button.
@aow3xm thanks for sharing that snippet. I would add this to ensure you don't get pointers on disabled buttons:
@layer base { button:not([disabled]), [role="button"]:not([disabled]) { cursor: pointer; } }
savior!
Try explaining this to 99% of product managers, QA, any non techs or just run with the above mentioned hack with targeting all buttons. I wish this decision was up for community discussion because I do consider this to be a breaking change.
I tried telling my lead designer that they changed the pointer behavior cuz of the ux and he laughed saying how is the user supposed to know it's a button? especially with ghost and sometimes the outline variants.
Currently, I find that button.tsx in shade/cn doesn't have cursor-pointer attribute.
In the existing tailwind v3, there was a button cursor attribute globally, but in v4, the disappearance from the global attribute seems to be the cause.
So I solved it used that code in tailwind v3.
@layer base {
[role="button"],
button {
cursor: pointer;
}
:disabled {
cursor: default;
}
}
I think cursor attribute should be added to shad/cn's default global setting!!
Closing this issue since it's not really a bug is nice, but still, where should we debate about this weird behavior then ? Is there a feed somewhere ?
You should not close since even shadcn said he is thinking on this.
Yes reopening. I'm working on this.
I trust @shadcn
Don't talk about "standards". Standards are dead, but people are alive. Removing the default cursor pointer of a button is simply the opposite of heaven.
UI design is diverse. It is not necessarily a button if there is a circular or square background with a few words on it. It can also be a key point or content that needs to be highlighted, or even just a "design".
Removing the cursor pointer of the button not only violates the most basic user experience principles, but also attempts to change people's long-standing habits. What's funny is that this modification is just to meet a standard from N years ago?
I was browsing around to understand this and even in the Tailwind pr #8962 asking to not use cursor: pointer on buttons the example they give in Linear apparently no longer itself uses default for buttons but back to pointer
The ship has sailed for defaults, users now expect buttons to have a pointer not just links
I don't know what kind of devil opened Tailwind, but cursor:pointer for buttons is currently literally everywhere and clients expect it too. Referring to standards from 15 years ago is so strange, especially since the authors of Tailwind seem to be people who base their decisions on research rather than personal preferences.
Anyway, thank you @shadcn for keeping your finger on the pulse and thinking logically. I appreciate it.
@aow3xm thanks for sharing that snippet. I would add this to ensure you don't get pointers on disabled buttons:
@layer base { button:not([disabled]), [role="button"]:not([disabled]) { cursor: pointer; } }
Thanks for sharing this 👍 - Out of curiosity - why is this your approach instead of just adding the Tailwind class to the Button component?
I want to add my 2 cents to this: To me it feels incredibly irritating if I don't get feedback when hovering over an element. If it looks like a button and behaves like a button it should also quack like a button - the cursor staying the same to me always feels like as if website has locked up.
Yes reopening. I'm working on this.
Thanks @shadcn. Do you plan on making all clickable elements in shadcn ui curosor:pointer? I hope so!
I understand the affordances and the standards history. But the vast majority of computer users believe:
if something is clickable, I see that hand pointing cursor".
So the UX should align with that. Imo we shouldn't rashly meet standards at the cost of a good UX.
Hi ! Any update on this @shadcn ? :-)
Classic case of bureaucracy over common sense.
@aow3xm thanks for sharing that snippet. I would add this to ensure you don't get pointers on disabled buttons:
@layer base { button:not([disabled]), [role="button"]:not([disabled]) { cursor: pointer; } }
Thanks for this.