# ScrollArea
`scrollarea` is a **scrollable region** with styled, auto-hiding overlay scrollbars — nicer than
the browser's default bars, and consistent across macOS, Windows, and Linux. Give it a fixed
height with `h` (and/or width with `w`) so its content can overflow and scroll. Use `type` to
control when the bars appear and `offsetScrollbars` to inset the content so the bars never
overlap it. Close the block with `{% endScrollarea %}` (one capital S).
Use it as `{% scrollarea %}…{% endScrollarea %}` in Markdown, or call it
from Python logic (loops, snippets) via `component('aardvark', 'scrollarea', …)`.
## Demonstrations
Constrain the height with `h`; the block body is the (overflowing) content. `type='always'`
keeps the styled bar visible.
**Preview:**
A tall block of content that overflows the 160px-tall area and scrolls.
Mantine's ScrollArea draws its own overlay scrollbar instead of the browser's, so it looks the
same on macOS, Windows, and Linux.
Keep scrolling — there's more here than fits in 160 pixels of height, which is the whole point
of constraining the area and letting the body overflow.
The bar tracks your position and (with `type='hover'`) fades out when you're not interacting.
Source: Markdown
```aardvark
{% scrollarea h='160' type='always' %}
A tall block of content that overflows the 160px-tall area and scrolls.
Add enough paragraphs here and the styled scrollbar appears on the right.
{% endScrollarea %}
```
Source: Python
```python
component('aardvark', 'scrollarea',
h='160', type='always',
children='A tall block of content that overflows the area and scrolls.')
```
### Scrollbar visibility and offset
`type` controls when the bars appear — `always` keeps them visible, `hover` (the default) shows
them only while you interact, `scroll` shows them while you scroll, and `auto` shows them only
when content actually overflows. `offsetScrollbars=true` insets the content so the bar never
overlaps it; `scrollbarSize` sets the bar thickness in px.
**Preview** — `type='auto'`, so the bar appears only because this content overflows:
With `offsetScrollbars`, the content is inset so the bar never overlaps it.
This area uses `type='auto'`, so the scrollbar appears only because the content here is taller
than 120 pixels — remove a few lines and the bar would disappear.
Source: Markdown
```aardvark
{% scrollarea h='120' type='auto' offsetScrollbars=true scrollbarSize=10 %}
With offsetScrollbars, the content is inset so the bar never overlaps it.
{% endScrollarea %}
```
Source: Python
```python
component('aardvark', 'scrollarea',
h='120', type='auto', offsetScrollbars=True, scrollbarSize=10,
children='With offsetScrollbars, the content is inset so the bar never overlaps it.')
```
## With other components
Wrap a tall stack of [Paper](/components/layout/paper/) surfaces in a `scrollarea` to keep a long
list within a fixed-height panel while the rest of the page stays put.
**Preview:**
First item
Second item
Third item
Fourth item
Fifth item — scroll to see the rest
Source: Markdown
```aardvark
{% scrollarea h='140' type='always' p='xs' %}
{% paper withBorder=true p='sm' radius='sm' mb='xs' %}First item{% endPaper %}
{% paper withBorder=true p='sm' radius='sm' %}…more items…{% endPaper %}
{% endScrollarea %}
```
Source: Python
```python
items = ''.join(
component('aardvark', 'paper', withBorder=True, p='sm', radius='sm', mb='xs',
children=f'Item {i}')
for i in range(1, 6)
)
component('aardvark', 'scrollarea', h='140', type='always', p='xs', children=items)
```
## Attributes
Omit any attribute to take its default. Bare flags (e.g. `offsetScrollbars`) become `=True`.
| Attribute | Valid values | Description |
| --- | --- | --- |
| `type` | `hover` (default) / `scroll` / `always` / `auto` / `never` | When the scrollbars are shown. |
| `scrollbarSize` | An integer (px) | Scrollbar thickness. |
| `scrollHideDelay` | An integer (ms) | Delay before the bars hide (with `type='hover'` / `scroll`). |
| `offsetScrollbars` | `true` / `false` (default `false`) | Inset the content so the bars don't overlap it. |
| `h` / `w` | A size token or CSS length | Height / width of the viewport — give it an `h` so the content can overflow. |
| `miw` / `mih` / `maw` / `mah` | A size token or CSS length | Min/max width and height. |
| `m` / `mt` / `mb` / `ml` / `mr` / `mx` / `my` | A Mantine size token or CSS length | Margin (all / top / bottom / left / right / horizontal / vertical). |
| `p` / `pt` / `pb` / `pl` / `pr` / `px` / `py` | A Mantine size token or CSS length | Padding (all / top / bottom / left / right / horizontal / vertical). |
| `bg` | A theme color or CSS color | Background color. |
| `attr={…}` | An object of HTML attributes | Forwards raw HTML attributes onto the rendered element. |
## CSS Selectors
Each `scrollarea` carries `data-aardvark-island="ScrollArea"` on its wrapper, and Mantine exposes its parts as `mantine-ScrollArea-*` classes — target either to style it from your theme CSS.
```css
[data-aardvark-island="ScrollArea"] {
/* style every scrollarea on the page */
}
.mantine-ScrollArea-root {
/* the root part */
}
.mantine-ScrollArea-viewport {
/* the viewport part */
}
.mantine-ScrollArea-scrollbar {
/* the scrollbar part */
}
```
## Injecting Attributes
`attr={…}` forwards raw HTML attributes (including event handlers) straight onto the rendered element.
Lots of content to scroll through.
Source: Markdown
```aardvark
{% scrollarea h='160' type='always' attr={'onclick': '''
const value = this.innerText;
console.log('attr demo value:', value);
alert(value);
'''} %}
Lots of content to scroll through.
{% endScrollarea %}
```
Source: Python
```python
component('aardvark', 'scrollarea',
h='160', type='always',
children='Lots of content to scroll through.',
attr={'onclick': '''
const value = this.innerText;
console.log('attr demo value:', value);
alert(value);
'''})
```