Next.jsでChakraUIを使っての開発時にフォーカスとそのスタイルで意外と気にすることが多かったので紹介します。
Next.jsでページ間リンクにフォーカスがつかない
Next.jsでサイト内遷移のリンクを作成するとき、next/link
を使います。
普通に使うとこんな感じですが、
import Link from 'next/link'
export const Hoge = () => {
return (
<Link href={'/about'}>
<a>About Us</a>
</Link>
)
}
リンクにChakraUIのスタイルを使いたい場合や、ChakraUIの書式でスタイルをつけたい場合は、a
タグのかわりにChakraUIのリンクコンポーネントを使います。(他のタグに置き換えない場合はa
タグで出力されます)
import Link from 'next/link'
import { Link as ChakraLink } from '@chakra-ui/react'
export const Hoge = () => {
return (
<Link href={'/about'} passHref>
<ChakraLink>About Us</ChakraLink>
</Link >
)
}
このとき気をつけたいのが、passHref
です。Next.jsのLinkコンポーネントの直下の子要素がコンポーネントである場合、passHref
を付けないとa
タグにhref
が出力されません。
<!-- passHrefがあるとき -->
<a href="/about">About Us</a>
<!-- passHrefがないとき(遷移は普通にできる) -->
<a>About Us</a>
https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-custom-component-that-wraps-an-a-tag
If the child is a custom component that wraps an<a>
tag
そしてhrefがないとフォーカスもつきません。
最初これに気づかずローカル環境だからhrefがないのかな…などと考えていましたが、passHref
です。
Next.jsが提供しているESLintプラグインにこれをチェックするものも含まれています。適宜使いたいです。
overflow hiddenするとフォーカスが欠ける
ChakraUIのUIコンポーネントのフォーカスは、特にカスタマイズしていない場合外側にbox-shadow
でスタイルがつきます。
このスタイルはboxShadow='outline'
で任意の場所に使うことも可能です。
<Box boxShadow={'outline'} p={6} rounded={'md'} bg={'white'}>
Outline
</Box>
ChakraUIを使ってカスタムコンポーネントに対してスタイルを書く中で、LinkやButtonの親要素にoverflow: hidden
を指定するとフォーカスのスタイルが要素の外側につくため、フォーカスが一部表示されなくなることがあります。
boxShadow='outline'
のようなフォーカススタイルでなんとか表示できるようにしたい場合は、boxShadow
にinset
をつけると内側にShadowをつけられます。
<Link
href={'/piyo'}
_focus={{
boxShadow: '0 0 0 3px rgba(66, 153, 225, 0.6) inset',
outline: 'none'
}}
>
piyo
</Link>
フォーカススタイルの見た目が違うコンポーネントがある
ChakraUIのコンポーネントを素直に使っている場合は問題ないのですが、Box
をbuttonにして使ったりするとBox
にはフォーカス時のスタイルがないので、フォーカスするとブラウザデフォルトのoutline
についたスタイルになります。気にする場合は_focus
のスタイルを設定します。
<Box
as={'button'}
_focus={{
boxShadow: 'outline'
}}
>
hoge
</Box>
input要素にフォーカスがつかない
ChakraUIあまり関係ないです。
type="file"
のinputにスタイルをつけるとき、label
タグで囲うとラベルをクリックして動作するinputになるのですが、フォーカスがつきません。labelにはフォーカスしないためです。
buttonなどのフォーカスする要素で見た目を作成し、その操作にinputが連動するような実装にするとフォーカスするUIにできます。
export const FileInput = () => {
const inputRef = ref as React.MutableRefObject<HTMLInputElement>
const clickHandler = () => {
inputRef.current.click()
}
return (
<>
// フォーカスが表示されない
<FormLabel cursor={'pointer'}>
<Input
type={'file'}
opacity={0}
visibility={'hidden'}
onChange={onChange}
/>
</FormLabel>
// フォーカス表示される
<Box as={'button'} type={'button'} onClick={clickHandler}>ファイル添付</Box>
<Input
type={'file'}
opacity={0}
visibility={'hidden'}
ref={ref}
onChange={onChange}
/>
</>
)
}
フォーカスをキーボード操作時のみにしたい
フォーカスをキーボード操作時のみにしたい場合は、focus-visible
が使えます。
ChakraUIのstyle propsにも_focusVisible
があるので、これを使えば指定できます。
<Tab
_focus={{
boxShadow: 'none',
}}
_focusVisible={{
boxShadow: 'outline',
}}
>
hoge
</Tab>
まとめ
ChakraUIは元々フォーカスに限らずアクセシビリティに考慮されて作られています。そのためコンポーネントを素直に使えばわりあい快適なUIが実装ができるのですが、自分でカスタムコンポーネントを作り始めるとそうはいかなくなってきます。なるべくうまくやりたいものです。