PhiaUi.Components.ScrollArea (phia_ui v0.1.16)

Copy Markdown View Source

ScrollArea component — a styled, accessible scrollable container. Zero JavaScript.

scroll_area/1 wraps any content in a div with controlled overflow and styled scrollbars. It uses the tailwind-scrollbar plugin (or equivalent) to apply thin, unobtrusive scrollbar styles that look consistent in both light and dark mode using semantic Tailwind tokens.

The component is a pure CSS primitive — no JS hook, no event handlers. All behaviour is handled by the browser's native scroll engine.

Scrollbar Plugin

The scrollbar-* utility classes require the tailwindcss-scrollbar package in the host project's Tailwind config:

# In your host project
# Install: npm install --save-dev tailwindcss-scrollbar
# tailwind.config.js
module.exports = {
  plugins: [require("tailwindcss-scrollbar")]
}

Without the plugin, the component still functions — scrolling works — but the native platform scrollbars are displayed rather than the thin styled versions.

Orientations

ValueCSS AppliedBest For
"vertical"overflow-y-auto h-fullTall content lists, sidebars, feeds
"horizontal"overflow-x-auto w-fullWide tables, code blocks, timelines
"both"overflow-auto2D content: maps, large diagrams

Example — Vertical Scroll (Default)

The most common use: a fixed-height container that scrolls when content exceeds the available height.

<%# Sidebar navigation with many items %>
<.scroll_area class="h-[calc(100vh-64px)]">
  <nav>
    <.sidebar_item :for={item <- @nav_items} href={item.path}>
      {item.label}
    </.sidebar_item>
  </nav>
</.scroll_area>

<%# Message feed in a chat panel %>
<.scroll_area class="flex-1 min-h-0">
  <div class="p-4 space-y-3">
    <.chat_message :for={msg <- @messages} message={msg} />
  </div>
</.scroll_area>

Example — Limited Height List

Show at most a certain height of content and scroll for the rest:

<.scroll_area class="max-h-64">
  <ul class="space-y-1">
    <li :for={item <- @items} class="px-3 py-2 hover:bg-muted rounded-md">
      {item.label}
    </li>
  </ul>
</.scroll_area>

Example — Horizontal Scroll for Wide Tables

Prevent layout breaking on small screens by wrapping wide tables:

<.scroll_area orientation="horizontal">
  <table class="min-w-[800px] w-full">
    <thead>...</thead>
    <tbody>...</tbody>
  </table>
</.scroll_area>

Example — Both Axes (2D Content)

For content that may exceed bounds in both dimensions:

<.scroll_area orientation="both" class="h-96 w-full max-w-2xl border rounded">
  <div class="min-w-[1200px] min-h-[600px]">
    <%# Large diagram, map, or canvas-like content %>
  </div>
</.scroll_area>

Example — Inside a Popover or Sheet

scroll_area/1 is commonly used inside popover_content/1 or sheet/1 to constrain long lists in confined spaces:

<.popover id="assignee-picker">
  <.popover_trigger popover_id="assignee-picker">Assign</.popover_trigger>
  <.popover_content popover_id="assignee-picker">
    <p class="text-sm font-medium mb-2">Select assignee</p>
    <.scroll_area class="max-h-48">
      <.command_item :for={user <- @users} on_click="assign" value={user.id}>
        <.avatar src={user.avatar} alt={user.name} size={:xs} />
        {user.name}
      </.command_item>
    </.scroll_area>
  </.popover_content>
</.popover>

Sizing

scroll_area/1 alone does not restrict height or width — you must provide a size constraint via the :class attribute:

  • class="h-64" — fixed height of 256px
  • class="max-h-64" — grows up to 256px, then scrolls
  • class="h-full" — fills parent container height (parent must have height)
  • class="flex-1 min-h-0" — fills remaining flex space (flex parent required)

The default h-full from orientation_class/1 assumes the parent has a defined height. Override with class="max-h-{n}" for most use cases.

Accessibility

  • The div itself is not focusable or interactive — it is purely a scroll container. Screen reader users navigate the content inside normally.
  • If the scrollable region contains interactive content, ensure keyboard users can reach all items within (Tab order works through the content).
  • For very long lists, consider adding a skip link or grouping mechanism so keyboard users can navigate efficiently.

Summary

Functions

Renders a styled scrollable container.

Functions

scroll_area(assigns)

Renders a styled scrollable container.

Scrollbar appearance is controlled by the scrollbar-thin, scrollbar-track-transparent, and scrollbar-thumb-muted-foreground/30 Tailwind classes. These use semantic color tokens so the scrollbar automatically adapts to dark mode and custom theme presets.

The overflow-* class and the primary dimension class (h-full or w-full) are determined by orientation_class/1 based on the :orientation attribute. Your :class override is appended last (via cn/1) so it wins in any conflict.

Attributes

  • orientation (:string) - Scroll direction(s):

    • "vertical" (default) — overflow-y-auto h-full; use for lists, feeds, sidebars
    • "horizontal"overflow-x-auto w-full; use for wide tables, code blocks
    • "both"overflow-auto; use for 2D content (maps, large diagrams)

    Defaults to "vertical". Must be one of "vertical", "horizontal", or "both".

  • class (:string) - Additional CSS classes merged via cn/1. Use this to set a height or width constraint — the component does not restrict dimensions on its own:

    • class="h-64" for a fixed 256px height
    • class="max-h-64" to grow up to 256px and then scroll
    • class="h-full" to fill the parent container (parent must have height)

    Defaults to nil.

  • Global attributes are accepted. Extra HTML attributes forwarded to the scroll container <div>.

Slots

  • inner_block (required) - Scrollable content.