next/linkのlegacyBehaviorを使ってaタグの中にbuttonタグが入らないようにする
next/linkが提供しているLinkコンポーネントを使用していた際、挙動としては問題なく動くもののHTMLの仕様的によろしくない実装をしてしまっていました。
具体的にはaタグの中にbuttonタグを入れ子にしてしまっていました。
MDNのaタグのページを見るとPermitted content
の欄にinteractive contents
をいれてはいけないと書いてあります。
buttonはinteractive contents
に相当するのでまずそうです。
入れても特にconsole上でエラーとかは見られなかったんですが、おそらく役割が不明瞭になるという点でアクセシビリティ上よくないってことなのではと推測しています。
というわけで、これを解消するというのが本記事の主題です。
ただ内容はNext.jsの公式ドキュメントに書いてあるlegacyBehavior
を使う方法で、特に目新しいものはありません。
HTMLの仕様を意識してコードを書こうという自戒を込めて記事にしておきます。
問題になっていたコード
Next.jsのv13ではnext/linkで提供されているLinkコンポーネントがaタグを内包するようになりました。
画面遷移するUIを表現する際、見た目がボタンであることはわりと一般的なんじゃないかなと思います。
今回もそのようなUIだったので、当初以下のように画面遷移するボタンを表現していました。
(ちなみにButton
コンポーネントはMantineというUIライブラリのものを使っています)
しかし、これだとLinkタグがaタグを内包しているのでレンダリング結果は以下のようになってしまい、aタグの中にbuttonタグが含まれてします。
これはHTMLの仕様的によろしくないので対策しないといけません。
legacyBehaviorを使う
対策の仕方としてはbuttonタグを使わずにボタンの見た目になるような実装が必要です。
頑張ってCSSでゴリ押ししてもいいんですが、あまりやりたくないので今回はUIコンポーネントの仕様を活かす方向で考えます。
MantineではButtonコンポーネントにcomponent
のpropsがあり、component="a"
とすると見た目そのままにaタグとしてレンダリングされます。
Next.jsのv13から導入されたlegacyBehabior
はv12までの仕様に戻すものであり、つまりLinkコンポーネント自体がaタグを内包しなくなります。
これらを組み合わせると無事、見た目はこれまで通りボタンでaタグとして画面遷移するものが出来上がります。
(passHrefは小要素のhref
propsにLinkのhrefを渡すためのもの)
ちなみにコンポーネントをaタグにしなくても挙動としては変わらないが、その場合はbuttonタグで画面遷移していることになってしまうので避けています。
buttonタグどころかコードをパッと見た感じだとおそらくonClick
が生えているものならなんでも良さそうなので、内部でonClick
で画面遷移させていそう?(ちゃんと読み解けておらず、自信はないです…)
まとめ
aタグの中にbuttonタグが入らないように回避する方法として、今回はbuttonコンポーネントを見た目そのままにaタグにしてlegacyBehavior
を使いました。
MantineのButton
コンポーネントにaタグにするためのpropsがあってよかった…
あとこれまでHTMLの仕様とかそんなに意識せずに書いてしまってたなーという反省。
できるところからちゃんと意識して書いていこうと思いました。