:focus-ringの代用としてwhat-inputを試す
前回の記事で紹介した:focus-ring
のポリフィルはイマイチだった。element.focus()
で制御したときにいい感じにならないというつぶやきを見て知った。
具体的な例として、モーダルを閉じた後の挙動について考えたい。
モーダルを閉じた後は、フォーカスはモーダルを開いたボタンに戻るように実装する。そのために、button.focus()
のようにしてフォーカスをスクリプトで制御する。この際、:focus-ring
のポリフィルだとfocus-ringが有効になる処理が実行されず、キーボード操作をしていても適切なスタイルが表示されないことになる。
調べてみるとWhat Input?という似たライブラリがあった。ユーザーの入力端末を検出する機能があり、同じくfocus-ringの制御をすることが目的らしい。:focus-ring
のポリフィルとは違い、入力端末の検出結果をhtml
要素の属性などを通して公開している。フォーカスのスタイルはそれに基づいて設定すればいい。これを利用すれば、意図した通りにフォーカスのスタイルを機能させることができた。
提供される状態は、initial
、mouse
、keyboard
、touch
のいずれかだ。initial
はまだ検出できていないことを示す。マウスとタッチデバイスではoutline
を非表示にしたいので次のようにする。
[data-whatinput="mouse"] :focus, [data-whatinput="touch"] :focus { outline: none; }
また、E:focus-ring
と同じ意図を示すセレクタは次のようになる。
html:not([data-whatinput="mouse"]):not([data-whatinput="touch"]) .awesome-button:focus { // focus-ring style }
ただこれでは冗長だ。E:hover
と併記したいことも考えると、Sassで次のように抽象化できる。
@mixin focus-ring() { html:not([data-whatinput="mouse"]):not([data-whatinput="touch"]) &:focus { @content; } } .awesome-button:hover { background-color: red; } .awesome-button { @include focus-ring() { @extend .awesome-button:hover; } }
多分これで問題なく:focus-ring
風の実装ができるはずだ。将来的に未知の入力端末が登場しても、マウスとタッチデバイス以外ではフォーカスのスタイルが表示されるため、ウェブサイトが操作不能になる可能性は低いと思う。
CSSのセレクタが複雑になってしまう問題はあるが、フォーカスのスタイルを非表示にしたいという要望とのトレードオフだろう。
本当は:focus-ring
がネイティブで実装される日を待ちたいが、これが今のところの現実解なんだと思う。