Scroll area
Scrolls long or wide content inside a bounded box. The scrollbar is drawn thin and adapts to the light/dark theme; no separate JavaScript library is needed. Optionally, the edges that still have hidden content behind them fade out softly.
Vertical scrolling
When the content exceeds the max-height value, only the inside of the area scrolls and the page stays in place. Pass the value as a CSS length (15rem, 50vh) or a number of pixels; a class such as max-h-60 does the same job. The area receives focus with Tab and scrolls with the arrow keys and Page Up / Page Down. When label is given, screen readers announce the area as a region with that name.
<acun:scroll-area max-height="15rem" label="Recently sold products" class="rounded-sm border border-gray-200 dark:border-zinc-700">
<ul class="divide-y divide-gray-100 dark:divide-zinc-800">
@foreach (['Wireless headphones', 'Mechanical keyboard', 'USB-C charger', '27-inch monitor', 'Ergonomic chair', 'Laptop stand', 'Webcam', 'External SSD 1 TB', 'Bluetooth speaker', 'Smartwatch'] as $product)
<li class="flex items-center justify-between gap-4 px-4 py-2.5 text-sm">
<span class="text-gray-900 dark:text-zinc-100">{{ $product }}</span>
<span class="text-xs text-gray-500 dark:text-zinc-400">September {{ 20 - $loop->index }}</span>
</li>
@endforeach
</ul>
</acun:scroll-area>
| Prop | Values |
|---|---|
max-height | CSS length or number of pixels |
height | Fixed height, in the same format |
label | Screen reader name (role="region") |
Horizontal scrolling
direction="horizontal" scrolls the content sideways; the width of the area is set by the parent container. The content must keep its own width, so wrap the row with w-max. It scrolls with a finger on touch screens and with Shift + wheel on a mouse. To scroll in both directions at once, use both.
<acun:scroll-area direction="horizontal" label="Category summaries">
<div class="flex w-max gap-3 pb-3">
@foreach (['Electronics' => 42, 'Apparel' => 18, 'Home & living' => 35, 'Beauty' => 27, 'Sports' => 22, 'Books' => 31, 'Toys' => 56] as $category => $orders)
<div class="w-48 rounded-sm border border-gray-200 p-4 dark:border-zinc-700">
<p class="text-xs text-gray-500 dark:text-zinc-400">{{ $category }}</p>
<p class="mt-1 text-xl font-semibold text-gray-900 dark:text-zinc-100">{{ $orders }} orders</p>
</div>
@endforeach
</div>
</acun:scroll-area>
| Prop | Values |
|---|---|
direction | vertical | horizontal | both |
Edge shadows
With shadows on, an edge that still has hidden content behind it fades out softly; when you reach the start or the end, that edge becomes sharp again. Because the fade is a mask, it does not depend on the background color of the card, drawer, or page. A small Alpine listener tracks the scroll position; it also recalculates when the content grows or shrinks or when Livewire adds new rows.
<acun:scroll-area max-height="12rem" shadows label="Customer notes" class="text-sm">
<div class="space-y-3 pe-3">
<p>The customer was called; the return request was approved and the return shipping code was emailed.</p>
<p>The billing address was changed to the company address. The accounting team was recorded as the contact person in case of need.</p>
<p>A discount was requested for a bulk purchase. The request was passed on to the sales team; a reply is pending.</p>
<p>The next order will be gift-wrapped; the note should appear on the warehouse team's screen.</p>
<p>Because the customer is not at the address on weekdays, deliveries should be rescheduled for Saturdays.</p>
</div>
</acun:scroll-area>
<acun:scroll-area direction="horizontal" shadows label="Category filters" class="self-start">
<div class="flex w-max gap-2 py-1">
@foreach (['All', 'Electronics', 'Home & living', 'Apparel & accessories', 'Beauty', 'Sports & outdoors', 'Books & stationery'] as $category)
<acun:badge :variant="$loop->first ? 'primary' : 'secondary'">{{ $category }}</acun:badge>
@endforeach
</div>
</acun:scroll-area>
| Prop | Values |
|---|---|
shadows | true | false |
Inside a card and a drawer
In a card, remove the body padding with :padding="false" so that the area extends to the edge of the card. In a drawer, turn off the body's own scrolling with body-class="flex min-h-0 flex-1 flex-col" and give the area min-h-0 flex-1: the header and the footer then stay in place, and only the content between them scrolls.
<acun:card title="Notifications" :padding="false" class="max-w-sm">
<acun:scroll-area max-height="13rem" shadows label="Notifications">
<ul class="divide-y divide-gray-100 text-sm dark:divide-zinc-800">
@foreach (['Order #10482 delivered.', '3 new reviews added to Wireless headphones.', 'Order #10475 shipment delayed.', 'Support ticket assigned to a new agent.', 'September sales report is ready.', 'Kadıköy warehouse stock list updated.', 'Monthly invoice summary sent.'] as $notification)
<li class="px-5 py-3">{{ $notification }}</li>
@endforeach
</ul>
</acun:scroll-area>
<acun:slot:footer>
<acun:drawer.trigger name="scroll-area-history"><acun:button variant="white" size="sm">Open full history</acun:button></acun:drawer.trigger>
</acun:slot:footer>
</acun:card>
<acun:drawer name="scroll-area-history" title="Order history" body-class="flex min-h-0 flex-1 flex-col">
<acun:scroll-area shadows label="Order history" class="min-h-0 flex-1 p-5">
<ol class="space-y-4 text-sm">
@foreach (range(1, 18) as $day)
<li>
<p class="font-medium text-gray-900 dark:text-zinc-100">September {{ 19 - $day }}, 2026</p>
<p class="text-gray-500 dark:text-zinc-400">Kadıköy warehouse · {{ 4 + $day % 5 }} orders shipped</p>
</li>
@endforeach
</ol>
</acun:scroll-area>
<acun:slot:footer>
<acun:drawer.close><acun:button variant="white">Close</acun:button></acun:drawer.close>
</acun:slot:footer>
</acun:drawer>
Props and slots
The values the component accepts.
| Prop | Default | Description |
|---|---|---|
direction | vertical | vertical · horizontal · both: the direction to scroll in |
max-height | null | Maximum height: a CSS length (16rem, 50vh) or a number of pixels; a max-h-* class can be passed instead |
height | null | Fixed height, in the same format as max-height; an h-* class can be passed instead |
shadows | false | Fades the edges that have hidden content behind them (with Alpine) |
label | null | Screen reader name; when given, the area becomes role="region" |