🦒

shadcn/ui(Radix Primitives)のScrollAreaでmax-heightと可変の高さを設定する

2024/12/15に公開

この記事は株式会社ガラパゴス(有志)アドベントカレンダー2024の15日目の記事です。

日頃からお世話になっているshadcn/uiですが、時々痒いところに手が届かない場面があり、カスタマイズして利用しています。

今回は、Scroll Areaについて、max-heightを指定したい場面があったのでその実装方法を紹介します。shadcn/uiのScroll-areaはRadix PrimitivesのScroll Areaから作られています。

https://ui.shadcn.com/docs/components/scroll-area
https://www.radix-ui.com/primitives/docs/components/scroll-area

完成したUI

下記のUIでは以下を満たしています。

  • <ScrollArea />max-heightを当てていて、固定のheightは当てていない。
  • max-heightの値になるまでは、コンテンツによって高さが可変である。
  • max-heightを超えたらscroll可能になる。
  • scroll可能になったら<ScrollBar />を表示する

完成UI

<ScrollArea />max-heightを有効にする

残念ながら、shadcn/uiのデフォルトの構成では<ScrollArea className="max-h-72" />とするとスクロールができなくなります。

ただし、もともとのRadix Primitivesのissueを見ると、<ScrollArea.Viewport />max-heightを指定するとスクロール可能になるそうで、実際にshadcn/uiの構成のViewportに指定したらスクロール可能になりました。

https://github.com/radix-ui/primitives/issues/2307#issuecomment-1654571158

しかし、ScrollAreaを各所で実際に使用しており、<ScrollAreaPrimitive.Viewport />を別コンポーネントとしてexportしたり、Viewport用のclassNamepropsslot分割はしたくない動機がありました。

そこで参考になったのが下記のissueコメントでした。

https://github.com/shadcn-ui/ui/issues/296#issuecomment-2063777401

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つの変更により、

  • Rootflex flex-colを指定
  • Viewportflex-growを指定(h-fullから変更)

これにより、Root明示的にheightを渡さなくてもViewportRootに合わせた高さになってくれます。

デフォルトのViewporth-fullの場合、Rootheightを指定しないと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": ユーザーがスクロールしている時、またはスクロールエリアにホバーした時にスクロールバーを表示します

https://www.radix-ui.com/primitives/docs/components/scroll-area#root

これは単に<ScrollArea type="auto" className="max-h-72" />とすることで解決できました。

結び

冒頭でshadcn/uiを"痒いところに手が届かない"と表現しましたが、むしろ利用者がカスタマイズできる柔軟性こそがshadcn/uiの素晴らしいコンセプトだと感じました。そして、Radix Primitives と Tailwind CSS にも感謝したい2024年冬。

GitHubで編集を提案
株式会社ガラパゴス(有志)

Discussion