Hacker News new | past | comments | ask | show | jobs | submit login
When going somewhere does a thing: on links and buttons (kilianvalkhof.com)
190 points by brandrick on Oct 11, 2022 | hide | past | favorite | 166 comments



My rule of thumb: if you could ever imagine a user wanting to control-click open in new tab or right-click open in new window, it should be a link. It's infuriating how many modern sites don't let you do this when the thing you're clicking on clearly ends up displaying a different view that you can independently navigate to in another tab or window.


I would like to second part of this more concisely for emphasis because I think designers too often get caught up and let usability suffer for the sake of "usability".

Please, for the love of god, let open up anything on your site in a new tab.


I think the worst design is when it's a control-click that will open a new tab, but the SPA trips itself up and goes back to the homepage/index

nothing is more painful when, after scrolling through an control-clicking some things that might be what you're looking for, seeing 5 or more tabs open, and then realising that they're all just the default homepage.


> Please, for the love of god, let open up anything on your site in a new tab.

Meanwhile, I've seen plenty of enterprise software and even banking sites, that:

  * try to prevent you from having more than one tab open
  * try to prevent you from having more than one session open
I get that sometimes server side state can throw a spanner into the works otherwise, when you can't write a proper stateless app/API, or write any kind of an app well enough either, but it's very annoying when you run into one of those systems.

More so, sometimes the explanation for this is "security", which just feels like the people behind the system design wanted to be lazy in regards to various lifecycles in the application. If I have my internet banking site open, pay for some goods in an e-commerce store in another tab through the banking integration and then refresh the banking site, me being signed out of it feels like a design flaw, not a "security" feature.

But oh well, you don't really get a say in these things, your bank is going to do whatever they feel like in regards to their UI/UX, as will those behind whatever piece of enterprise software that you have to use, or that you'll have to write in accordance with the requirements.

It's up there in regards to annoyance, much like ctrl clicking some element, just to discover that it doesn't let you open up new tabs, or maybe even right clicking something or trying to select text, just to realize that neither works and you have to copy whatever you need out of the source of the page.


It's not really just about (UI/UX) design, to be fair, being able to open in a new tab requires that it's a directly navigable 'page' by URL to begin with. With SPAs, that's often not true for entire 'pages', never mind modals and views you might not be expected to want in a new tab but do.


When users want to open things in new tabs (hopefully having a state representing URL), then there might be something about the website, that does not actually want to be a single page web application. Sometimes users simply want to "open things for later viewing" or bookmark things. Basic functionality of any modern web browser.


couterpoint: users don't care if your page is SPA. They care about opening new tabs.


Exactly. It don’t care if it takes more work for you as a developer to make everything a pageable URL. Just make it work the way I expect it to work.


... and developers don't care if users like new tabs, they write SPAs.


After spending a day trying out a new framework my junior dev asked me: "why don't we do it the old way? We would have finished the first feature already. We can do everything the framework does already with our old tools"

So I reverted my changes and worked on features instead of stack


Quite a good thing for a junior dev. Not being swept away by hype and being focussed on the thing one actually should accomplish. I don't know that person, but this smells like there is potential.


That junior developer’s name? Albert Einstein.


Give that junior a promotion.


I'm not defending it! I hate it too!

I was just responding to it being a criticism of 'designers', maybe I misread that, but my initial reaction was as above, that it's not completely trivial to allow, or like it necessarily comes for free and has been 'turned off' by design.


I made almost the exact same comment as GP elsewhere.

One thing that I noticed is that more than half the time "open in new tab" is broken, I can click the link, copy the URL, open a new tab, paste the url and get what I wanted. It's so maddening.


Often the problem if you develop something as a SPA then your framework probably has different state management solutions built in that you will develop with, without any way to easily tie that state to a particular url or to pass a bunch of state to a new window. Thus opening new window puts you in default state of the SPA.

This is of course maddening if the site should not be a SPA but was developed that way because when you are running a company and need to build things quickly it is easier if you just have one toolbox labelled SPA-building tools and pull that out and get going.


That's a quite fair point, but as a counter point in most business cases you do want specific views (even specific searches and user applied filters) to be addressable - it's one reason why I think the history/location API is such a necessity for SPAs. And if you do have an address like example.com/#widget/view?hideWhitespace=false then you can bind your link href to that address and then, if the link is engaged without modification, do your fancy SPA stuff.

I also think in the modern world that as long as your assets are all properly versioned with good cache control headers using actually web requests for navigation instead of a self-contained SPA makes a lot less sense. Once upon a time an SPA was a solution to shifty internet, limited bandwidth and rich web experiences - those arguments have become a lot less valid in the modern world.

(this is a topic extremely near and dear to my heart as a (eh-sorta) full stack developer that needs to suffer through many poorly built applications as a user)


> It's not really just about (UI/UX) design, to be fair, being able to open in a new tab requires that it's a directly navigable 'page' by URL to begin with.

I'd argue that each individual view in your app should have a routable URL, otherwise sharing links to anything is basically impossible, as is bookmarking certain pages, or even reloading them.

> With SPAs, that's often not true for entire 'pages', never mind modals and views you might not be expected to want in a new tab but do.

Some claim that modals are bad design and I wholeheartedly agree, though for a variety of reasons they still keep showing up across numerous systems. I guess a compromise could be giving the modal open state its own URL so the app would know what to do if the page is refreshed (the modal would still be open), but somehow that doesn't get much attention either.

An especially clever option (for niche cases) would be being able to open a modal in a tab/window of its own, without the surrounding UI, when opened directly through the URL/new tab/new window, but show it inline in the app during normal navigation. Then again, many dislike pop-ups either because that's not really the direction in which web development went, though I can see why in certain sites they could be useful (e.g. CRUD apps with lots of boring tables and element selection). Oh well.


There are plenty of SPAs that can open things in new tabs just fine and there are plenty of MPAs that can't.


Another annoying pattern I've encountered in some online shops: The product list technically uses proper links that you can left/right/middle-click to your heart's content, except they've also attached an "onclick" listener to the link, so even if you've right/middle-clicked because you actually wanted to open a new tab, the current page still starts navigating to the link target, too.


This is mostly a developer mistake, not that of a designer. You can easily make buttons appear as links, so there's no excuse not to handle this the correct way.

You also have to disconnect styling from HTML tags for your headings to get the h1>h2>h3 order semantically correct for accessibility and SEO.

Regardless, I think designers making links appear as buttons is bad practice, but you usually don't have much influence over design as a developer.


Why would the visual hierarchy of your headings not also reflect the semantic hierarchy you implement for accessibility and SEO?


Im a new react developer and it's extra work figuring out how I'd update the page from which you created the new tab without replacing it to trigger a state change. In the case of sites that render data synced to a datastore. Using a timer, sure, but thats not wildly efficient. There must be ways, they're just an extra consideration.


You can sync localstorage and sessionstorage between tabs, you only have to listen to the right events. I haven’t used indexeddb much, but I’d be surprised if that doesn’t sync between tabs as well. If you are saving all the state in memory—which I think is the default for most popular frameworks—then you’ll have to do something more clever. If you have a backend server as a single source of truth, then you should probably be using websockets if it is vitally important that your users get real updates of new data. I’m sure there are popular react libraries that will handle each of these cases.

It is extra work for sure, but usability improvements usually are, and if you know what you are doing, this will not cost that much extra in maintenance.


links that go to other "pages" (even just conceptually) should get their own route so you can link to them directly. you can use something like react-router to do this, and it's very easy to extract the active route and know what view to render. or if I'm totally off the mark, maybe I misunderstood what you mean :)


I don't understand.. opening on a new tab should do exactly the same as opening a blank tab, typing the address and pressing enter

In particular it shouldn't change the original page at all, that's the whole point!

Now, if the original page is the kind that updates in real time (due to accesses from other tabs or even from other machines), just use a web socket!


Note: Coming from someone who hates when middle click doesn't open a new tab... so I get why you care.


Can't browsers automate this by duplicating the current tab and then clicking the link?


Even more infuriating when you attempt to implement correct usage of links/buttons & get pushback such as "what's control-click? no one does that. non-issue."


It's a hard fight to fight, certainly, but a common tactic I've employed to counter this is "Web standards exist for a reason and we don't want our website called out for being inaccessible to the blind community. Additionally - these standards have been revised and refined over decades and I don't feel comfortable subverting some of these rules without considering them all - it may break our compatibility with some browsers."


I can tell you from firsthand experience that the people demanding the wrong implementation do not understand the purpose of web standards, much less care about the rationale for following them. On the front end I have seen unimaginably poor designs be implemented with as little justification as "Our pilot users said the UI 'looks cleaner' to do it this way [the wrong way]" and the management is satisfied with that.


Hence why I harp in on two specific points "You don't want to piss off blind people" and "You don't want cross browser compatibility to suffer" - most technical background managers will hear the second point and have a flashback to IE6 or IE4 and immediately start backpedaling, while non-technical background managers will hear the first point and wonder if AADP will end up suing them.

The statement I made is not a genuine expression of the most important reasons - it is a tool to persuade management using cherry picked boogiemen to scare foklks into the right decision... aka a bad faith argument made for the best of reasons.


It gets even worse with azure devops. It tracks which repository/branch/feed/board you looked at last and then shows that one when you go the respective features. Which means that when you copy the url it does not always include your exact location and you need to do dance to switch the repository or branch to another and back in order to get it in the url.

To make it worse ctrl/cmd click for opening it in a new tab doesn’t always work. Sometimes it open the repo you clicked on in a new tab, sometimes it opens a new tab but with your most recent repo, and sometimes it just opens the repo in the current tab. It is infuriating…


The modern web us utterly broken in this sense and the culprit is SPAs. At this point it seems like the road has no return as the new generations of devs will have no idea how the web of even 10 years ago was behaving. Maybe it’ll all come down under it’s own weight one day.


The culprit is bad frontend developers and sometimes design. Nothing wrong with SPAs, and it's not that hard to make them usable.

Azure DevOps is still utterly shit though.


Are there actual use cases where Azure cloud is the right choice?


Azure the cloud and azure dev ops are two separate but integrated things from the same company. DevOps itself runs in azure.

I think there are plenty of reasons to choose Azure Cloud. Maybe Azure Devops flows from that. But it is far from my favourite tool.


Single page apps have bastardized the web and reduced consistent usability in so many ways such as your example.


Prior to SPAs we had multi-page applications telling the user "don't use the back button or tabs, they break the website". Was that really better?


Not all websites got it wrong. They are both a problem but one does not excuse the other in regards to ignoring the way the web spec works.


Not all SPAs do it wrong either, that's my point. Caring about the user is orthogonal to the technology used.


Most SPAs get it wrong because it’s complex to make work well in a single page framework. It’s nearly impossible to get wrong with a traditional webpage.


False dichotomy.

A poor implementation of a generally sensible approach is not justification to embrace a more complex and error-prone approach.


This argument can be used word-by-word in favor of SPAs and against earlier web applications, but it's a No True Scotsman either way.


But then you’re making the argument that a SPA is less complex than a more orthodox equivalent, and I don’t think that’s the case.


It was definitely the case for most applications I have worked with because they had to perform client-side logic on the server side. Most SPAs I've seen at least have clearly separated responsibilities between the two, AND they don't act as if there wasn't a network in between.


> AND they don't act as if there wasn't a network in between

…But there is a network in between. I'm not sure why pretending there isn't can somehow make a system less complex.

Furthermore, how do you write automated tests for your modals and slide-out panels? Your test suite needs to essentially run a browser. This is absolutely more complex than having an application on the server compute the document that the client expects.


> But there is a network in between

That's exactly my argument...? Seems to be a misunderstanding.

> I'm not sure why pretending there isn't can somehow make a system less complex.

Yes, that is exactly why pre-SPA web programming was more complex.

> Furthermore, how do you write automated tests for your modals and slide-out panels? Your test suite needs to essentially run a browser. This is absolutely more complex than having an application on the server compute the document that the client expects.

How do you test that the HTML/JS generated by the server actually show a modal? At least with libraries like React you can test everything up to the actual CSS engine. And no, you don't need a browser for that.


I don't think either of us are going to convince the other. I've built large, complex systems with both approaches, and I'm assuming you have too.

> How do you test that the HTML/JS generated by the server actually show a modal?

You can test for the existence of markup on page load. You can't test that some JavaScript for displaying a modal has run without running a JavaScript runtime, i.e., a [headless] browser.

In most cases, just loading a page which displays the appropriate information for the user is cheaper to test and implement, and usually is a better experience for the user also. It depends of course — this would not be true of an application like Google Maps. Most web applications aren't Google Maps though.


Grubhub is one such site when trying to cmd+click on a restaurant.

It's absurd their engineers haven't fixed something so simple.


Perhaps they don't want you to be able to do that?


yes! Far too many sites have terrible experiences when you 'try to got back to something'. back button shenanigans, full page reloads, unable to retrieve data, and worst: losing something that you were half way through writing or doing.

I just want everything to always open in a new tab (or sometimes a new window). gmail yep, music sites yep, youtube yep.


I know it's not what you want necessarily but if you didn't know right clicking on the back button will give you a lot more options and let you go back to various points in history. I find it necessary quite often because I believe that ublock fucks with javascript and breaks a lot of "back button" stuff. I still am never gonna give it up though.


I have a thread on Twitter where I've been keeping track of times that I've run into this.

https://twitter.com/gramofdata/status/1268661032890896387

One of the most annoying examples I encounter on a regular basis is in Twitter's trends sidebar, where they don't use links, but instead have manually added event listeners for ctrl-click and middle-click.


I usually click mousewheel to open new tab


Sometimes command+click will still work to open into a new tab when a right-click does not.


Sometimes Command+Click will open in a new tab when Middle Click doesn't work, and sometimes Middle Click will open in a new tab when Command+Click won't work.

If you're using Gmail, you get both types of behavior on the same page!


Isn’t middle click a user defined function? Is it system defined? I assume you could assign ctrl/cmd-click to the middle button if you want. I’ve not worked on a system with a middle mouse button in many years.


I haven't owned a mouse without the ability to middle click ever (my first mouse had a ball and 3 buttons; I think a Logitech c7?).

If your mouse has a scroll-wheel, then it's probably clickable as a middle-click. If you have a trackpad without a 3rd button then there is usually a gesture that will send a middle click.


And some of those times, the parent still navigates so you get duplicate tabs. Ugh!


The thing that bothers me about this whole insistence that, for accessibility purposes, it is vitally important that we correctly employ links and buttons to ensure that users of assistive technologies can correctly anticipate whether an interaction will go somewhere or do something...

Why do only assistive technology users get treated with that level of respect?

Because let's be clear - for sighted users interacting with this person's webpage, whether the markup is a link or a button, they evidently plan that visually it's just going to be a paint roller icon, inside an area of the screen of indeterminate size which is going to be somehow interactable.

Apparently it's not important to consider whether I, a user not currently employing assistive tech, might need to know before I click it whether that control will cause a page navigation, carry out an operation, or what.

It might be a link; it might be a button; it might just be a decorative picture of a paint roller. It might interact on hover, on click, or on double click. Who knows!

This definition of accessibility as something distinct from usability, where frontend devs will torture themselves over the semantic markup that they use to ensure clarity of purpose for accessibility purposes, has somehow become completely divorced from the world of UX design, where visual indications of affordances are no longer seen as valuable.


Once upon a time, links had an underline and buttons had a beveled frame. We seen to have somehow come to accept the visual designers’ claim that, for some reason, we can't do that anymore.


Who cares if it's usable; it's pretty!


It’s inconsistent and unintuitive


Sighted users normally have a whole bunch of CSS to convey the same information (as is recommended by most [all?] accessibility standards). Users of assistive technology have to rely on the semantics of the document.

Also sighted users get this information as well. If you hover over a link, it displays the target href in the lower corner or your window.


The article does not mention changing what the control looks like, only how it is marked up. The design is shown as settled - a roller icon - and the only question to be decided is if that icon is implemented as a link or a button in the DOM.

And there’s no hover-link-pop up on a mobile phone touchscreen.


Well actually the article is wrong on the markup, it should be neither a link nor a button, but a <details> <summary>


> Also sighted users get this information as well. If you hover over a link, it displays the target href in the lower corner or your window.

You shouldn't rely on that: it won't work on mobile nor for users who don't want or can't use a mouse (e.g. people with a motoric disability).


The target usually shows as well if you navigate using the keyboard. But you are right, I shouldn’t rely on that, and I don’t, I style my links with CSS so that sighted users can distinguish them by their styles. The location is a bonus. Also if sighted mobile users want to see the location, I think they can click and hold the link, and see it in the context menu.


I actually think the link solution in the Codepen box shows perfectly what's wrong with the link solution: Links should (and do by browser default) maintain their history state, while buttons do not. If you don't want to maintain the change in back button state, don't use a link.

If in his link example I click the "Open Theme Controler" link, then click the "Close" link, and do that a couple times, when I then click the back button to come back to Hacker News, I'm instead cycled through the number of times I clicked those links. I doubt any user would expect that to happen.


I was writing this corollary to the new tab rule of thumb, with the exact example of CodePen embeds, but backed out to see if someone else added it already. Drives me bonkers every time my browsing history gets polluted by storing local state in the URL.

And now that I think of that phrasing, I think that probably the unifies the rules of thumb: if it changes local state, it’s a button. If it changes local scope, it’s a link.


That’s because they implemented the link using history.pushState (default non-js link behaviour) when history.replaceState should be used instead.

It might still be valid to use a link for an on-page modal, for instance when it needs to be navigationable or should persist on a refresh.


> (default non-js link behaviour)

I mean, that's pretty much my whole point. Forget javascript (as the example in the article does), but links by default append to the state, because that is pretty much how browsers always worked before they even exposed the history API to javascript.


IMO:

If it makes sense to "open in new tab" then it should be a link. Otherwise it's a button.

Corollary: If it's a link then middle-clicking on it better open it in a new tab! There are way too many times that this was broken by JS with the href just being set to "#" or something, but clicking the link, copying the new URL, opening a new tab, pasting the url, going back to the original page and hitting "back" did exactly what I wanted. Don't do this!


> If it makes sense to "open in new tab" then it should be a link. Otherwise it's a button.

The problem is that apparently, an infuriating number of Front end devs seem to think that nothing in their shouldn’t-have-been-an-SPA page should ever be a link and go somewhere, preferring instead to waste an equivalent amount of time popping up some modal or changing page anyway.


This is a very popular criticism of SPAs, but every time I see it I really gotta ask how people are screwing this up and if it actually has anything to do with SPAs. With every even slightly popular way I know of to develop SPAs, you have to go out of your way to make links not work as normal links.


You don't make a single page application by adding links to other pages.


Sure you do. You use the Link component (or similar) that’s provided by the SPA router, which renders a normal <a href="cool-page"> tag that works fine with middle-clicking, command-clicking, and right-clicking. The only difference is that it overrides the click event to perform a client-side navigation on normal clicks. You have to go out of your way to make this not work, or I suppose you could implement your own SPA framework and not understand how any of this works.


I’ve never liked the <Link> approach. I much prefer to write normal links throughout and intercept clicks globally, which is easier to work with, and every bit as reliable (more, in some situations). See https://news.ycombinator.com/item?id=31366875 for some discussion of this approach, with sample code in https://news.ycombinator.com/item?id=31373486.


Yeah, that works well too, it just isn't as common in component-based frameworks for the reasons pointed out in that thread.


But I also debunked those reasons in-thread: the URL is fundamentally global state, so there’s no virtue in avoiding a global event handler—in fact, there is virtue in using a global event handler, because the alternative encourages inconsistency, with regular page-load routing accidentally not working in parts of the system (and I’ve definitely seen this happen in real life).


But a lot of times they have a useful routing library (even if by accident) that updates the URL, so opening a new tab with the update URL actually does something useful, but it's not a link so I can't do that.


Ugh, its an annoying problem that I understand from both sides. Opening a new page has a surprisingly large amount of user friction vs opening a sidesheet or modal. Most of it is just a deficiency in how browsers work. If I'm scrolling down a listing page, I don't want to open a new page because returning will often leave me not where I was before. So I make a judgement call on if a link is _really_ worth clicking. While pages which allow expandable previews or modals, I am much more likely to click since its less of a disruption.


Returning/navigating back absolutely takes you back exactly where you were. It’s broken because the whole page is now being rendered in js and that forces a re-render. SPA misuse has ruined web to the extent we’ve forgotten what it was like to have proper navigation before all the history hijacking and complete rerendering jazz.


On the other hand, after opening a bunch of tool windows and documents to copy-paste between in photoshop or blender or whatever, users don’t expect a ‘back button’ to take them back to the previous window layout state. They might expect a back button to navigate among which window is in the foreground, or between view/zoom positions.

Consider the possibility that some web applications might actually benefit from acting more like that.


Why I said SPA misuse and not use of SPA.


> If I'm scrolling down a listing page, I don't want to open a new page because returning will often leave me not where I was before.

Have you actually tried doing that on this very website you are now reading?

Click on my profile. Then click back. Where are you?

When I tested this just now, I was returned to exactly the point in the page where I left it.


It works reliably for pages that don't do any client-side-rendering – in that case the browser reliably knows when it has finished loading and layouting all the content and can then restore the scroll position.

Things become more iffy with client-side rendering, because the browser has no clear signal to decide when page-loading has progressed far enough that it can try to restore the previous scroll position.


Right. So do less client-side rendering. The internet does way too much client-side rendering, for no reason whatsoever.


That's not going to happen. It's much easier to open a model or sidesheet than use server side rendering. I imagine in future generations of frameworks and browsers, we will be able to do SPAs without the issues we currently see.


I really don’t think it is. You then need to account for:

- all the extra styling complexity

- the history API

- memory management (sometimes)

Etc.


Or simply, if it is a form <button> make it look like a button. Otherwise if it is an <a> make it look like a link.

Also related: if it is a GET request, don't mutate state or perform an action on the server (except perhaps logging). But that is basic security too.

There are exceptions, but know why you are doing the exception. For example a landing page will typically have button-like links for call to actions, because people expect that.


If it's an <a> don't just make it look like a link, make it act like a link instead of having the href just be "#" and overriding what it does with an onclick().


This is a much better rule of thumb IMO.

I actually have been a proponent of replacing a bunch of buttons with links at a previous workplace, using the middle click as an argument in favor.

Sibling comments seem to misunderstand a frontend development a little. I’ve replaced quite a few dialog modals with pages (in a single page app), and it is usually because a backend developer (or a well meaning intern) thought they could implement the feature on their own (or more likely a project manager told them to), and simply didn’t known about the usability issues with this. Most front end developers I know deal with the same stuff in their workplace.


So when do I use `<div onclick>`? /s

It’s disappointing how developers are so lazy to not even know the basic concept of a link. It’s completely ridiculous yet super common to see click handlers on DIV elements that set the value of location.href

As for A/BUTTON, we really need an attribute that clears the button’s style entirely, safely and forever. Nobody knows how to properly and full drop all useragent styles from a button, try googling it.


I keep wondering if this can be attributed to front-end devs learning SPA libraries (React, Angular, etc..), skipping the 'boring boilerplate' by attending boot camps that start them with a gigantic node_modules/ folder, and never actually learning the fundamentals of HTML, like anchor links (I doubt such developers even know that the 'a' tag stands for 'anchor'). So we end up with unsemantic div soup everywhere, and such developers breaking into a cold sweat when someone utters 'accessibility' in proximity to their demo.


This comes from the fact that SPA routers work best when you use JS to update so you don't have the page flash like a normal page load. React router at least has a trick element which is an a tag with href but if you click it, it intercepts it an does an onclick load. But it still works as a link for all other purposes like copying location.


Default computer peripheral behaviors to please not mess with:

- right-clicking defaults - scrolling defaults - Back/forward browser buttons to traverse _state_ as the user expects - tabbing (and leave the damn default outline!)

If it's not a video game, please don't F with my keyboard and mouse's default behaviors. Thank you.


Don't fuck with Cmd+F (or whatever the Windows/Linux equivalent of that is) either. If I press Cmd+F, I want the proven, fast, local, browser-based search with the consistent UI I'm used to. Not some shitty Javascript replica whose functionality I can't predict and in practice ends up disappointing.


In my opinion, a link - as clickable by a user - should never be able to mutate the state of the system.


That's pretty much exactly how the web was designed – an anchor link is a GET request, a button is a POST.

However, as the article points out, this analogy breaks down once you go beyond basic HTML and into complex UI layouts.


Gonna be a tough sell for verification emails.


Not really - the link points to a page containing a button.

You'd need to do this for practical reasons too, considering links in e-mails might get automatically fetched whether for malware scanning, caching or generating a link preview, and it would erroneously trigger your verification workflow.


We can just agree that it should be a button and is only a link because of the limited HTML subset available in emails.


Isn't the current browser path part of that state?


Arguably, yes.

What I was really trying to go for with "state" in this context was something more like "business state". Things on the server that you change and can see with a refresh on a different client.


If it’s a GET, it’s a link, if it’s a POST, it’s a button? :)


`role=button` is not a replacement for <button> or <input> either!


Why not?


Great question! The value of `role` tells the screen reader the element is a button, that's all. To be focusable, handle touch, mouse and keyboard (space and enter ofc) inputs you could write bespoke JS and CSS, or change `div` to `button`


This is all true and correct advice. I’m reticent to add this because, but for those curious about how close you can get to native <button>/<input type="button"> without the native form control, tabindex=0 gets you pretty much there. They’re all effectively useless without JS and all have the same styling capabilities these days, however, so better to just use the thing that’s designed to be a button unless you have a really special use case that you really understand (you don’t).


That doesn't quite get you there, though. For example, it doesn't submit a form it is contained in when clicked.

There are some use cases where a <button> won't work (eg if you want to use flexbox), but even then I'd first re-evaluate how important that use case is, then reach for a properly tested library to turn another element effectively into a button [1], and only then try to implement it myself and probably forget some built-im functionality, breaking things for users and costing me lots of time.

[1] https://react-spectrum.adobe.com/react-aria/useButton.html#c...


Flexbox not working in buttons is news to me (and I use display: flex and display: grid all the time). I just tested in both Chrome and Firefox and display: flex works just fine inside a button.

I think there was a bug in Chrome a couple of years ago where they incorrectly denied some element the ability to be flex containers (<fieldset> was a really annoying case of this) but I think that is fixed now.

The only case that I can think of where you can’t use a <button> is if you have nested interactive elements (a button within a link/button). There are sometimes ways around that (absolute positioning of the outer link/button) but sometimes there aren’t and then you need to re-implement a fake button from a <div>.


Oh hmm yeah, you're right, I must've mixed it up with another example in my mind. Ah well, replace that with your example in my comment, and the point stands :)


Indeed, I just remembered I actually have a SO answer where a “fake” button is one of the solutions ( https://stackoverflow.com/a/55155649/2444959 ). This question was about having an enabled button inside a disabled fieldset.

I actually do think that use cases are rare, and when you need a fake button, there are probably better solutions (in the SO question, the question author probably just wanted <details> and <summary>) but—though rare—these cases do exist.


> That doesn't quite get you there, though. For example, it doesn't submit a form it is contained in when clicked.

Er sorry, I meant type="button" for both. Neither submit a form or do anything other than show the default interactive styles without JS.

> There are some use cases where a <button> won't work (eg if you want to use flexbox)

I’m not sure what you mean, and having a hard time imagining what you might mean. Can you elaborate?


My example was wrong, your sibling comment [1] provided a better one.

[1] https://news.ycombinator.com/item?id=33173406


And supports "disabled" by default.


This button should have been a <details> <summary>.

And this rule of thumb is also incorrect (albeit by technicality). A form submit button goes somewhere (to the action response) and is a button.

I agree with the sentiment though, in general:

- If you are navigating through the page, use a link,

- if you are performing an action, use a button, (and in addition)

- if you are reveling some information, use a <details> <summary>


There are multiple reasons why details/summary are bad semantics, outlined in this article: https://adrianroselli.com/2019/04/details-summary-are-not-in...


This article is self admittedly out of date. And where it is not, it is listing opinionated widgets types the author doesn’t think that <details> <summary> is not a replacement for.

The OP widget is non of these, I would say it is an open question whether <details> <summary> is the right choice here.


Of straight HTML, <details> is certainly the most appropriate semantically (and it’s what I use on my site). This particular site would find it mildly difficult to apply since the picker body affects layout but is in a different part of the document to the button, but since its height is known it could just barely be done.

Another semantically-reasonable possibility which allows separation of source and destination would be using a button with appropriate aria-controls and aria-expanded to link it to the picker body; and probably giving that body the dialog role.


Yeah I agree. I probably would have used a non-modal dialog here. You can actually absolutely position (or, rather sticky positioned) the body of the <details> to the top.

But I think the dismiss button, inside the dialog warrants it being a <dialog>. Dialogs usually open with a <button> (so that answers that question), and you would open this by calling dialog.show()—as opposed to the usual dialog.showModal()—since you don’t want to capture the focus. The Benefit of using the <dialog> element is that you can put it anywhere you want inside your DOM structure and allow it to interact with the page however you want to.


I just learned about using this tag in github comments for hiding full tracebacks or large code blocks and it is such a usability game changer.


I think it must be a link if you would expect user want to open in a new tab, no matter it pushes history (navigation) or replaces history (in page tab).

There are countless of times that middle click to open new tab didn't work because page author use <button onclick="location.href='xxx'> as link. It's such an awful experience.


Not to get off-topic, but in the site footer the author mentions he's the developer of:

https://polypane.app/

Has anyone used it? Thoughts?


I'm using it all the time as my main development browser.

It's been awesome using it. It's not just responsive design, it has fantastic features around accessibility and performance, I can set different viewports to different speeds and colour contrasts all at the same time. It is bonkers how powerful it is as a tool. I always recommend it to people to try out.


Using it for quite a while now. It is amazing. Even 'just' using the multi device view is mostly enough for me, but it goes a lot further with accessibility audit functions and such.

I definitely recommend trying it out.


I actively use it.

I'm pretty familiar with responsive design so I'm honestly not using the multi-viewport size set up that much. But it can also show dark/bright view next to each other, set up viewports with different networks speeds, and toggle various media preferences which is just super handy as these settings aren't that easy to toggle in default dev tools.

A nice extra is the page info pane which makes it super easy to validate all the metadata + check how the social cards show on popular networks.


been using it for a while. As someone who came to frontend development later, and responsive design even later, polypane's multi device view itself has helped heaps. in fact i feel guilty that i'm not using its full capabilities. the biggest thing though is the dev responsiveness to issues or bugs or just stuff u couldn't figure out how to do and why


no, but I do use https://responsively.app/


There's also Sizzy (https://sizzy.co). It's available standalone, but it's also included with a Setapp subscription (https://setapp.com — Mac app subscription service).


yeah, sizzy is cool, it's what lead me to responsively as these monthly charges just don't match how I use the tools.


One major problem here is the mixup of UX and technical implementation details. From UX point of view, the link example goes nowhere, it just opens a dialog. From that point of view it the fact it uses anchors to do so is not really relevant.

From purely technical point of view, the question would be rather irrelevant as the distinction between a button and link is mostly how a human perceives it, it does not matter for the program.

This is likely an explanation for the awkwardness that the author mentions feeling of this implementation, and is supported by it not making sense to open this kind of a "link" in a new tab (because it does not go anywhere).


Personally, I use a <input type="checkbox"> to do that kind of thing, like the hamburger menu.


I do this too if I’m trying to play JS payload golf and the stakes are low for users, but I always feel guilty because I know it harms accessibility. JS is actually required to make an expandable/collapsible menu accessible, even if your clever pure-CSS solution seems bulletproof. It’s a shame, there really should be native controls for this kind of thing that don’t require arbitrary executable code.


This is precisely what <details> <summary> is for. No javascript required. It gives the correct semantics for assistive technology, correct interactions with the keyboard, focusability, etc. I recommend you use it over the old checkbox hacks or javascripts.


Yes! This example is a control that toggles the visibility of part of the page. It is neither a link nor a button

And, you know, if you think about this from an assistive technology point of view, does a screenreader user actually need to worry about interacting with a control to make something visible? Isn't it better from their point of view to skip the interaction entirely and just put the list of theme options semantically under a navigable page heading?


In this specific example, is the visual theme of the page even relevant to a screenreader user at all?


A lot of screen reader users are partially blind, in the process of losing their site, or sharing a screen with someone else. So it’s better to give both experiences at the same time


It’s kinda cool but apparently you can’t really be accessible AND script-free because they say you should change some aria attributes and potentially some focus when you open modals like menu and dropdowns.

A #menu href coupled with :target style is technically more accessible because the focus naturally shifts to it. Then the exit button should point to #menu-toggle so that closing the menu brings the focus back to the origin.


Square.com uses similar looking links and buttons in a dark pattern to trick users.

Login and land on the Home page. You want to look at transfers to your bank account (Balance > Account). Here you see big blue link box taking your to Balance: https://postimg.cc/yD97T8yT

Sometimes this blue box is a button to initiate an early transfer, which triggers an additional early transfer fee: https://postimg.cc/t1jpvGrG

Once you get to the Balance page, you still need to find the link taking you to the Account page (View All Transfers) to actually see your transfers. Square takes a second stab at getting that fee with a big blue button in the middle of the page: https://postimg.cc/bG98KVMD

Now perform this same action several times a week, all year round, and you will make the mistake and press the transfer button.

It only takes once for you to have a never ending enmity towards Square for filching $25 from your income.

I was caught the once in October of 2021. I *recall* the transfer was initiated immediately (and the extra fee of 1.5%). Since then I've revisited the button when the transfer was low, and I was willing to risk $5 to get a screen shot of the next page. That time I did find a confirmation screen (I can't find the screen shot now).

I call support, but they would not refund the transfer, despite having pressed it by accident, despite having never used the feature before, and having over five year relationship with about 200k/yr in sales going through their platform. "But you're getting the service" was their reply.


I’ve written a tutorial on building one of these that uses a checkbox.

There’s more ways than a button or a link. Some people have suggested in the comments using details and summary.

It works without javascript, represents the states open and closed without requiring any aria attributes and some other usability features people might find interesting.

https://endtimes.dev/html-and-css-only-multiple-color-scheme...


GitHub uses <details> for most of their dropdowns and it does generally work well, though you'll run into issues if you ever need to break out of the DOM hierarchy (for example, to have the popup overflow a scrollable container).


I made a big button that does something by going somewhere. https://gloutir.com/


Just make everything a link (a tag) unless you can't. Doesn't matter how it looks like. It can be some text with underline or it could look like a button.

Even if the button only does some action on the page, it could still be represented with a url. Sometimes it doesn't make sense. Then that's the time to use something other than an a tag.

If this were the default, most of this problem wouldn't exist.


For example, Pronhub doesn't let you ctrl/cmd click images or links. You can right click and pick "open in new tab" tho


Middle click works. It's a shame that input devices have devolved for the sake of style and fashion.


yeah middle click is underutilized by most netizens. One click to open and close them all and in the darkness bind them.


Thankfully, there are browser add-one to restore right-click functionality.


WCAG is kinda subpar. Last time I reached out definition of "owned" element and found there are open issues still discussing about it. In application context, I wonder if desktop apps are really accessible and no one complains about, or just the web got really bad rep partly because they are more accessible..bad examples.


Are those two things always mutually exclusive?


The article states that they are not in the sentence immediately below the part where this quote was sourced.


The article addresses that


I used to be more adamant that links should never open modals and should be buttons. Until we ran into an action column in a grid having 4-5 items. Having it be all buttons would look ridiculous and annoy users with space usage. We went with link tags (some are true links and some are modals). Not a peep from users.


Sometimes it does both — e.g. when I use Front and click on an email, it does something. But I should ALSO be able to open it up in a separate frame, for long readings (in Front you do this by double clicking).

Is it good practice to use links, then intercept then with preventDefault when it should go somewhere AND do something?


I respect this distinction in almost all cases. However, login and signup feel like they deserve to be styled as buttons, perhaps because they are the first step of a flow that will eventually have side-effects. However, they are just links on the home page.


A stock broker once tricked me into to executing a trade when I visited a link they sent on email--no validation; no login; no confirmation beforehand. I visited the "unique URL", and that was it.


Kinda unrelated. This is more of an issue with that brokering site triggering an action on a GET visit, not related to HTML


Dare I ask, in the title, what is meant by the word "it". The word is used four times. However the meaning does not seem consistent across each usage.


Link == new screen. Button == mutate existing screen.


Links can go to another location on the same screen, or a geolocation, email; any URL


Seemed to me links jump somewhere on the same or on a different page and buttons do stuff though the line is blurry, for example on a form submit a new page could render..


Form submissions should be links?


Could we change the submission title please? The entire article is in fact debunking this point.


Just use <div> [doge]




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: