概要
WebフロントエンドにてUIコンポーネントを作り直す機会に多く恵まれ、その中でどういうコンポーネントにすると再利用性が下がるのかを発見できたので紹介したい。
以下、React, JSXを利用しているものとする。
z-indexを利用したコンポーネント
z-index を利用するとコンポーネントに閉じない影響を他コンポーネントに与える。
どこかのコンポーネントで z-index を利用するとそのコンポーネントを利用しようとしたときに、常に z-index を考慮した実装に気を付けなければならない。
stylelint や変数化などして無策に利用するのは避けたい。
floating-uiでz-indexを利用しないのもオススメ。
https://floating-ui.com/
1以上の数のtabindexを利用したコンポーネント
tabindex で1以上の数を設定すると、コンポーネントに閉じない影響を与える。z-index同様に、コンポーネントを利用するときに注意が必要だ。さらには、タブキーによるフォーカスでユーザーに違和感を与えない試行錯誤が必要にもなる。
eslintで避けられるので設定するのがオススメである。
https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/tabindex-no-positive.md
左側が前面で重なった表現を利用したコンポーネント
通常、重なりをそのままcssで実装しようとすると、position: relative や absolute を利用することになる。その時の表現としては、右側が前面になるような見た目のUIになる。
これに対して、左側が前面になるようなUIを表現するには、z-index をそのまま利用するか、左側に配置したいUIを右側に配置したいUIよりも、敢えて下に記入して position を指定する方法がある。
<div>右側後面</div>
<div>左側前面</div>
z-index は先に述べたように利用しないことが望ましい。ただ、左側前面になるものを右側後面になるものよりも下に書くと、タブキーによるフォーカスが逆転する(右側後面→左側前面の順になる)。
tabindexを付けて対応することもできるが、tabindex はコンポーネントに閉じない影響をあたえるため、この方法も望ましいといえない。
かがんさんの記事で、親にz-index: 0を付けることでこの問題を回避することができることを知った。
https://zenn.dev/kagan/articles/1aa466bb6ef8eb
複数のコンポーネントを含む全体がクリック可能なコンポーネント
イメージは下記の状態である。ClickableComponent にはbuttonが利用されいると仮定し、Component の中に aやbuttonがあった場合は、HTMLの仕様的に違反した書き方になる。インタラクティブなタグの中にはインタラクティブなタグを入れてはいけない。
https://www.w3.org/TR/2011/WD-html5-20110525/the-button-element.html
<ClickableComponent>
<Component />
</ClickableComponent>
この問題を回避する方法として、ClickableComponent が、aやbuttonなどを利用せずに、divやspanを使いクリック可能にする。
<div onClick={}>
{children}
</div>
ただ、これにも問題がある。クリック可能な要素としてbuttonタグを利用した時と比べて、タブキーによるフォーカスを充てられるようにとか、role設定とか、buttonじゃないものに対してbutton的に振舞わせる努力が必要だ。divにblur関数が無くエスケープでfocusを外す機構の実装は諦めた。
まとめ
いくつかのWebのUIコンポーネントとして再利用性を下げる具体的な事例を紹介した。
再利用性を高めるためには、仕様の検討するときにいずれのパターンを避け、できるだけ回避したい。
ただ、知識や実装力が高ければ、仕様で回避せずとも何とかなるかもしれない。