> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/ui/scroll-and-grid.md).

# Scroll and Grid

***

Scroll views and grids make it easy to build inventories, lists, menus, and card layouts. This guide shows the correct patterns for both, including layout, IDs, and scroll bars.

{% hint style="info" %}
Follow the UI fundamentals: start from screen rects, use cut for layout, and always use `defer` for push/pop pairs.
{% endhint %}

### Scroll Views

Use scroll views for content that can exceed the available space. The scroll bar must be outside the scroll view content area.

```go
draw_scrollable_list :: proc(items: []string) {
    rect := UI.get_safe_screen_rect().inset(50);

    // Reserve space for the scroll bar first
    scroll_bar_area := rect.cut_right(8).inset(2);

    settings: Scroll_View_Settings;
    settings.vertical = true;
    settings.horizontal = false;
    settings.clip_padding = {10, 10, 10, 10};

    sv := UI.push_scroll_view(rect, "my_scroll_view", settings); {
        defer UI.pop_scroll_view();

        content := sv.content_rect;
        ts := UI.default_text_settings();
        ts.size = 24;

        for i: 0..<items.count {
            item_rect := content.cut_top(40);
            UI.text(item_rect, ts, items[i]);
        }
    }

    scroll_bar_rect := sv.compute_scroll_bar_rect(scroll_bar_area);

    UI.quad(scroll_bar_area, core_globals.white_sprite, {0.025, 0.025, 0.025, 1});
    UI.quad(scroll_bar_rect, core_globals.white_sprite, {0.1, 0.1, 0.1, 1});
}
```

**Important:** If the scroll bar is inside the scroll view content, it will break the bounds and scrolling behavior. Always cut the scroll bar area out first.

### Grid Layout

Use `Grid_Layout` for equally-sized elements like inventory slots or icon grids. Grids start at the top-left, advance to the right, then wrap to the next row.

```go
draw_inventory_grid :: proc(items: []string) {
    rect := UI.get_safe_screen_rect().inset(40);

    bs := UI.default_button_settings();
    ts := UI.default_text_settings();

    // 4 columns x 6 rows based on element count, 8 points of padding
    grid := UI.make_grid_layout(rect, 4, 6, .ELEMENT_COUNT, 8);

    for i: 0..<items.count {
        UI.push_id("slot_%", {i});
        defer UI.pop_id();

        slot_rect := grid.next();
        if UI.button(slot_rect, bs, ts, items[i]).clicked {
            log_info("selected item %", {i});
        }
    }
}
```

If you prefer fixed-size tiles, use `.ELEMENT_SIZE` instead of `.ELEMENT_COUNT`:

```go
grid := UI.make_grid_layout(rect, 100, 100, .ELEMENT_SIZE, 8);
```

The `next()` method updates grid state:

```go
slot_rect := grid.next();
grid.index; // 0, 1, 2, ...
grid.cur_x; // column
grid.cur_y; // row
```

### Scrollable Grids

`Grid_Layout` automatically expands scroll views when used inside one. This pattern is ideal for large inventories:

```go
draw_scrollable_grid :: proc(items: []string) {
    rect := UI.get_safe_screen_rect().inset(50);
    scroll_bar_area := rect.cut_right(8).inset(2);

    settings: Scroll_View_Settings;
    settings.vertical = true;
    settings.horizontal = false;
    settings.clip_padding = {10, 10, 10, 10};

    sv := UI.push_scroll_view(rect, "inventory_scroll", settings); {
        defer UI.pop_scroll_view();

        grid := UI.make_grid_layout(sv.content_rect, 4, 6, .ELEMENT_COUNT, 8);
        bs := UI.default_button_settings();
        ts := UI.default_text_settings();

        for i: 0..<items.count {
            UI.push_id("slot_%", {i});
            defer UI.pop_id();

            slot_rect := grid.next();
            if UI.button(slot_rect, bs, ts, items[i].name).clicked {
                select_item(i);
            }
        }
    }

    scroll_bar_rect := sv.compute_scroll_bar_rect(scroll_bar_area);
    UI.quad(scroll_bar_area, core_globals.white_sprite, {0.025, 0.025, 0.025, 1});
    UI.quad(scroll_bar_rect, core_globals.white_sprite, {0.1, 0.1, 0.1, 1});
}
```

### Best Practices

* Use `UI.push_id()` for repeated elements inside lists or grids.
* Cut the scroll bar area before creating a scroll view.
* Start from `UI.get_safe_screen_rect()` and derive all layout.
* Keep hover-only interactions to a minimum (mobile-first).
