Chakra UIのLinkコンポーネントとfocus-visible疑似クラス
2021-11-28 公開
追記: Safari でも focus-visible がサポートされるようになりました
2022年3月にリリースされたSafari 15.4にて focus-visible
疑似クラスがサポートされるようになりました。
Safari 15.4 Release Notes | Apple Developer Documentation
Chakra UIにおいても、v2.2.0から要素へのフォーカス時のスタイルにデフォルトで focus-visible
を使うようになっています。
chakra-ui/CHANGELOG.md at @chakra-ui/[email protected] · chakra-ui/chakra-ui refactor: remove annoying focus outline by segunadebayo · Pull Request #6153 · chakra-ui/chakra-ui
本記事で紹介しているpolyfillも必要なくなったので、現在はこのブログにおいてもpolyfillを削除しています。
このブログでも使用しているChakra UIには多くのコンポーネントがあり、WAI-ARIA仕様に対応しているなどアクセシビリティが考慮されています。 しかし、実際に利用していると気になる点がありました。
Chakra UI の Link コンポーネントの挙動
Chakra UIの <Link>
コンポーネントはchakraによって拡張されたただの <a>
タグです。
Next.jsでのinternalなページ遷移で利用する場合は next/link
と併用する必要があります。
以下のコードでは、internalなページのときのみ next/link
で <Link>
をラップし、外部のページのときは素の <Link>
を使用する例です。
import NextLink from 'next/link';
import { Link } from '@chakra-ui/react';
const MyLink = (props: JSX.IntrinsicElements['a']) => {
const { href, ...rest } = props;
const isInternalLink = href && (href.startsWith('/') || href.startsWith('#'));
if (isInternalLink) {
return (
<NextLink href={href} passHref>
<Link {...rest} />
</NextLink>
);
}
return <Link isExternal {...p} />;
};
このコンポーネント自体は問題なく動作するのですが、 ヘッダーのような複数ページに渡って表示される場所に置いてクリックするとfocus時の青いアウトラインがページ遷移後も表示されたまま残ってしまいます。
この問題はChakra UIのIssueとして取り上げられています。
Blue outline borders around all clickable components ugly · Issue #708 · chakra-ui/chakra-ui
コメントによれば、Chakra UIはWAI-ARIA規格に厳密に従うことを目標にしており、focus時のアウトラインをデフォルトで無効にすることはない、ということです。 デフォルトで有効になっていること自体は納得できるのですが、Tabキーなどのキーボード操作ではないマウスクリックでもアウトラインが表示されるのは少し気になります。
_focus props を変更してアウトラインを無効化する
先ほどのIssueではいくつかの回避策が提示されています。 そのうちのひとつは、Chakra UIが設定しているfocusのスタイルを上書きする方法です。
個別の <Link>
コンポーネントを修正する場合は、_focus
propsにスタイルを追加します。
<Link _focus={{ outline: 'none', boxShadow: 'none' }}>Link</Link>
また、<Link>
コンポーネント全体でアウトラインを無効化したい場合、themeを拡張することでデフォルトの挙動を変更可能です。
import { extendTheme } from '@chakra-ui/react';
const theme = extendTheme({
components: {
Link: {
baseStyle: {
_focus: {
outline: 'none',
boxShadow: 'none',
},
},
},
},
});
これで <Link>
クリック時にアウトラインが表示されることはなくなりました。
しかし、この方法はキーボード操作によるfocusでもアウトラインが表示されなくなります。 できれば避けたい気持ちがありますね。
focus-visible 疑似クラスを使う
同Issueで多くリアクションを集めていたのは、focus-visible
疑似クラスを用いた回避方法でした。
以下の記事にまとまっています。
Accessibility on-demand with Chakra-ui and focus-visible | by Keegan Famouss | Medium
MDNのページには以下のように記載されています。
:focus-visible 擬似クラスは、要素が :focus 擬似クラスに一致している時で、ユーザーエージェントが要素にフォーカスを明示するべきであると推測的に判断した場合に適用されます (多くのブラウザーではこの場合、既定で「フォーカスリング」を表示します)。
このセレクターは、ユーザーの入力方法 (マウスなのかキーボードなのか) によって異なるフォーカス表示を提供したい場合に便利です。
今回の「マウスクリック時はアウトラインを無効化してキーボード操作にはスタイルを適用させたい」というユースケースに合致しています。
残念ながら、2021年11月現在 focus-visible
疑似クラスはSafariが対応していないので、モダンブラウザすべてで使うためにはpolyfillを使う必要があります。
以下、focus-visible - npm を使った設定手順です。
npm install focus-visible
oryarn add focus-visible
import 'focus-visible/dist/focus-visible'
を配置するfocus-visible
CSSをアプリケーションに適用する
Next.js + Chakra UIでの設定例は以下のようになります。
import { ChakraProvider } from '@chakra-ui/react';
import { Global, css } from '@emotion/react'
import 'focus-visible/dist/focus-visible'
const globalStyles = css`
.js-focus-visible :focus:not(.focus-visible) {
outline: none;
box-shadow: none;
}
`;
function MyApp({ pageProps }) {
return (
<ChakraProvider resetCSS>
<Global styles={globalStyles} />
<Component {...pageProps} />
</ChakraProvider>
);
}
上記の設定はこのブログサイトにすでに適用されています。 試してみたい場合はこのブログサイトのヘッダーのリンクをクリックしたりTabキーを押したりしてみてください。
まとめ
Chakra UIはアクセシビリティに配慮された使い勝手のいいコンポーネントライブラリですが、 デフォルトの挙動が気に入らない場合はスタイルをオーバーライドして変更可能です。
最新のCSSを活用することでアクセシビリティを維持したまま意図する挙動を実現できるので、polyfillが必要なくなるようにSafariでの実装が待たれます。