shadcn/ui(Radix Primitives)のScrollAreaでmax-heightと可変の高さを設定する
この記事は株式会社ガラパゴス(有志)アドベントカレンダー2024の15日目の記事です。
日頃からお世話になっているshadcn/uiですが、時々痒いところに手が届かない場面があり、カスタマイズして利用しています。
今回は、Scroll Areaについて、max-heightを指定したい場面があったのでその実装方法を紹介します。shadcn/uiのScroll-areaはRadix PrimitivesのScroll Areaから作られています。
完成したUI
下記のUIでは以下を満たしています。
-
<ScrollArea />にmax-heightを当てていて、固定のheightは当てていない。 -
max-heightの値になるまでは、コンテンツによって高さが可変である。 -
max-heightを超えたらscroll可能になる。 - scroll可能になったら
<ScrollBar />を表示する

<ScrollArea />にmax-heightを有効にする
残念ながら、shadcn/uiのデフォルトの構成では<ScrollArea className="max-h-72" />とするとスクロールができなくなります。
ただし、もともとのRadix Primitivesのissueを見ると、<ScrollArea.Viewport />にmax-heightを指定するとスクロール可能になるそうで、実際にshadcn/uiの構成のViewportに指定したらスクロール可能になりました。
しかし、ScrollAreaを各所で実際に使用しており、<ScrollAreaPrimitive.Viewport />を別コンポーネントとしてexportしたり、Viewport用のclassNamepropsやslot分割はしたくない動機がありました。
そこで参考になったのが下記のissueコメントでした。
const ScrollArea = React.forwardRef<
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
>(({ className, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root
className={classX('flex flex-col relative overflow-hidden', className)}
>
<ScrollAreaPrimitive.Viewport
ref={ref}
className="flex-grow w-full rounded-[inherit]"
{...props}
>
{children}
</ScrollAreaPrimitive.Viewport>
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
))
このコメントでは以下の2つの変更により、
-
Rootにflex flex-colを指定 -
Viewportにflex-growを指定(h-fullから変更)
これにより、Root明示的にheightを渡さなくてもViewportがRootに合わせた高さになってくれます。
デフォルトのViewportがh-fullの場合、Rootのheightを指定しないとViewportがコンテンツの高さに合わせられてしまい、overflow-y: scroll;が機能しなくなります。
これで、<ScrollArea className="max-h-72" />に指定することで、max-height以上になった時にscrollになることができました。
UXを改善する
このままでも実用可能なのですが、スクロール可否の状態を視覚的にユーザーに知らせたくなります。
RootのAPIにtypeがあります。デフォルは"hover"になっており、hoverするまでスクロールの可否は分かりません。
Describes the nature of scrollbar visibility, similar to how the scrollbar preferences in MacOS control visibility of native scrollbars.
"auto"means that scrollbars are visible when content is overflowing on the corresponding orientation.
"always"means that scrollbars are always visible regardless of whether the content is overflowing.
"scroll"means that scrollbars are visible when the user is scrolling along its corresponding orientation.
"hover"when the user is scrolling along its corresponding orientation and when the user is hovering over the scroll area.MacOSのスクロールバー設定のように、スクロールバーの表示方法を制御するプロパティです。
"auto": コンテンツがはみ出している場合にのみスクロールバーを表示します
"always": コンテンツのはみ出しに関係なく、常にスクロールバーを表示します
"scroll": ユーザーがスクロールしている時のみスクロールバーを表示します
"hover": ユーザーがスクロールしている時、またはスクロールエリアにホバーした時にスクロールバーを表示します
これは単に<ScrollArea type="auto" className="max-h-72" />とすることで解決できました。
結び
冒頭でshadcn/uiを"痒いところに手が届かない"と表現しましたが、むしろ利用者がカスタマイズできる柔軟性こそがshadcn/uiの素晴らしいコンセプトだと感じました。そして、Radix Primitives と Tailwind CSS にも感謝したい2024年冬。
Discussion