📄

Chakra UI の as プロパティを利用するときに知っておくこと

2024/12/04に公開

はじめに

Chakra UI では as プロパティを使うことで、 styled-components と同様に、要素のレンダリングを変更できます。

<Box as="button" borderRadius="md" bg="tomato" color="white" px={4} h={8}>
  Button
</Box>

引用: Box - Chakra UI

Box は HTML だと div 要素のため、 onClick プロップスを持たせるとき、 Tab キーで要素にフォーカスができなかったり、スクリーンリーダーで認識されなかったりと、アクセシビリティ的な観点で不都合が起きます。
これらはなぜ <div> に onClick がダメなのか?で詳しく解説されています。

as プロパティを使うと、 <Box> を button 要素など他の要素に変更できるため、 <Box> に適用したスタイリングを維持しつつアクセシビリティの問題を解消できます。

<Box> を使用していた場面で、 onClick を持たせるために、 Chakra UI の <Button> コンポーネントを使用すると、スタイルを維持するために <Button> コンポーネントにデフォルトでついているスタイリングを無効化する必要があり、コードが複雑になる恐れがあります。

一方で Chakra UI が提供する <Button> コンポーネントを使わずに as="button" で済ませた場合に、アクセシビリティや機能面で何か問題は発生しないのでしょうか?

結論: as="button" で済ませた場合、アクセシビリティ面では問題なさそうだが、機能面で手動で設定しないといけないものがある

as="button" でもフォーカスやスクリーンリーダーに対応しており、基本的なアクセシビリティ機能は備わっています。

一方で、 as="button" を使う場合は、isDisabled や loading といった状態管理やそのスタイリングは、手動で追加しないといけない場合があります。

as="button"の際にも備わっているアクセシビリティ機能

as="button" を使用すると、 HTML の <button> タグと同等になるため、フォーカス対応しており、スクリーンリーダーに対応もしています。

  • フォーカス対応: Tab キーでフォーカスされ、 Space もしくは Enter でクリックされた判定になります。
  • スクリーンリーダー対応: スクリーンリーダーにクリック可能な要素として認識されます。

検証: アクセシビリティ観点で差異はあるのか?

Chrome の開発者ツール( Elements > Accessibility タブ)で比較したところ、目立った差異はなさそうです。

検証 1 : 通常時の比較

<Button>Button Component</Button>
<Box as="button">Box Component with as="button"</Box>

Button コンポーネントの場合

Name: "Button Component"
 aria-labelledby: Not specified
 aria-label: Not specified
 From label: Not specified
 Contents: "Button Component"
 title: Not specified
Role: button
Invalid user entry: false
Focusable: true

Box コンポーネント( as="button" )の場合

Name: "Box Component with as="button""
 aria-labelledby: Not specified
 aria-label: Not specified
 From label: Not specified
 Contents: "Box Component with as="button""
 title: Not specified
Role: button
Invalid user entry: false
Focusable: true

比較しても、 Name と Contents 以外の差分は見当たりません。

検証 2 : 非活性時の比較

<Button isDisabled>Button Component</Button>
<Box as="button" disabled>Box Component with as="button"</Box>

Button コンポーネントの場合

Name: "Button Component"
 aria-labelledby: Not specified
 aria-label: Not specified
 From label: Not specified
 Contents: "Button Component"
 title: Not specified
Role: button
Disabled: true
Invalid user entry: false

Box コンポーネント( as="button" )の場合

Name: "Box Component with as="button""
 aria-labelledby: Not specified
 aria-label: Not specified
 From label: Not specified
 Contents: "Box Component with as="button""
 title: Not specified
Role: button
Disabled: true
Invalid user entry: false

こちらも、実装上は isDisabled と disabled とプロパティに差異はありますが、比較しても、 Name と Contents 以外の差分は見当たりません。

問題: 状態管理は自前で実装が必要

<Box> コンポーネントは loading のプロップスを持たないため、コンポーネントの状態管理を自前で実装する必要があります。

🙆‍♂️ 問題ない実装

<Button loading={true}>
  Click me
</Button>

🙅‍♂️ 不可能な実装

<Box as="button" loading={true}>
  Click me
</Box>

また、 <Box> で disabled プロパティを指定した際、 <Button> コンポーネントのようにデフォルトで disabled のスタイルが当たることがないため、自前でスタイルを実装する必要があります。

下図ではどちらも disabled にしていますが、 <Button> コンポーネントは disabled のスタイルが自動で当たっています。
 disabled 状態の比較図

上記 2 つ具体例を出しましたが、結論 Chakra UI の Button コンポーネントに備えられた便利なプロップスやデフォルトで設定されたスタイリングを利用できないという欠点が as="button" にはあります。

まとめ: as="button"を使うときは

<Box> に適用したスタイリングを維持しつつ、 onClick プロパティ付与に伴う、アクセシビリティの問題を解消したいシチュエーションであれば、特に気にせず as="button" を使用して問題なさそうです 🙆‍♂️

例えばカードをクリックすると詳細画面へ遷移する場面などで、as="button"はアクセシビリティの観点から大きな問題なく使用して良さそうです。

一方、送信ボタンや作成ボタンなど mutation や POST を叩くような一般的なボタンは、 isLoading や isDisabled プロパティがしっかり備わった Chakra UI の Button コンポーネントを使用した方がシンプルに実装ができそうです。

今回は大雑把に検証したので、別のユースケースなどでアクセシビリティや機能面で差異が存在するかもしれません。

もし他に Button と as="button" の違いがあればご教示いただけると嬉しいです。

ROUTE06 Tech Blog

Discussion