[W3C Compliance] Tailwind CSS Button with link is not W3C compliant
Hello! I'm having a problem with the current implementation of buttons WITH links, concerning Material Tailwind.
The documentation states to do:
<a href="#buttons-with-link">
<Button>Filled</Button>
</a>
(And it is ofc also possible to do):
<Link href="#buttons-with-link">
<Button>Filled</Button>
</Link>
https://www.material-tailwind.com/docs/react/button#button-with-link
:information_source: It's a mistake to document React components using
<a>for links, instead of<Link>, but that's another topic.
Using the code from the documentation presents an issue.
The W3C standard prohibits nesting a <button> within an <a> (and vice versa).
In the case of <a> inside <button>, it's a very poor choice anyway because the clickable area is no longer the button but only its text, which is very silly.
In the case of <button> inside <a>, it's awkward because it seems like the most logical way to do it.
You can try it yourself here: https://validator.w3.org/nu/#textarea
By running the validator with the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Lol</title>
<meta name="description" content="Lol">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<a href="#"><button>Click me!</button></a>
<button><a href="#">Click me!</a></button>
</body>
</html>
I'm sorry, I tried to edit my message to correct my suggestions, including the "Second solution" option, which I thought was the correct one but turned out not to be, and so explain why... But since GitHub is incapable of properly coding a message form and message edits, I ended up with my edited message all messed up.
I will add my rationale below the original post of my issue, I'm just taking a break before rewriting it properly and submitting it below.
Three solutions:
First solution (bad one)
Wrap the <button> within a <form>, using two attributes:
-
action, taking the URL, -
method, equal to"get"
<form action="https://example.com" method="get">
<button type="submit">Click me!</button>
</form>
This way to do has an advantage:
- It preserves the initial style of buttons, including those directly dictated by the machine's OS.
And a big disadvantage:
- If I write
<form action="https://example.com?lol=lulz&lulz=lol" method="get">
<button type="submit">Click me!</button>
</form>
By clicking the button, I will be redirected to https://example.com!
So, it's a poor choice when the URL contains parameters.
Second solution (seems good, but isn't)
Merge the <a> and <button> elements
Merge the <a> and <button> elements to create a <a>, styled as a button
<a class="button-style" href="#">Click me!</a>
This way to do has a big advantage:
- All links will redirect to the good URL.
And a minor disadvantage:
- We cannot inherit the OS buttons style.
I initially thought that the second solution was the good one, but it isn't. Here is one way to implement it:
- Get the parent node with
.parentNodeon the<Button>node - https://developer.mozilla.org/en-US/docs/Web/API/Node/parentNode - Check if this is a
<a>with.tagName(x.tagName === 'A') - https://developer.mozilla.org/fr/docs/Web/API/Element/tagName- Replace the parentNode, either with replaceWith, or by setting outerHTML. - https://developer.mozilla.org/fr/docs/Web/API/Element/replaceWith
But here's the catch:
Replace the parentNode, either with replaceWith, or by setting outerHTML. - https://developer.mozilla.org/fr/docs/Web/API/Element/replaceWith
I haven't found any way to do this properly with a new <Link> component, replacing the initial <Link>.
So, here is a third solution, which is simpler and probs the best way to do:
Third solution (finally the good one?)
Provide an optional argument
So we could conditionally render the <Button>, whether as a <Link to="_" target="_" {...{stylizedAsAButton}}>, whether as a regular <Button>.
Maybe it would be good to provide only a linkProps optional argument, as an object, and only check if linkProps and its to key are undefined (linkProps && linkProps.to) to know if we should render a <Link> or a <Button>. We can inject all the properties of linkProps with the spread operator.
Something like: <Link {...{linkProps}} {...{stylizedAsAButton}}>
Well, NextUI provides something which is incredible to solve this problem: polymorphic components.
See: https://nextui.org/docs/components/link#polymorphic-component