# llimphi-widget-scroll Reusable vertical scroll area. Until now every app rebuilt scrolling by hand: `App::on_wheel` + an offset in the `Model` + `clip` + virtualization. This widget packages that pattern into a single builder, **with no state of its own** (the offset still lives in the `Model`, true to the Elm loop): - a **clipped viewport** of fixed height (`viewport_len`), - **content displaced** by `-offset` px (overflow clipped), - a **draggable scrollbar** on the right (only when the content exceeds the viewport), - a **self-contained wheel** through `View::on_scroll`: turning the wheel with the cursor over the area emits a `Msg` without the app routing anything through its global `on_wheel`. The caller must know the content's **total height** (`content_len`) and the **visible height** (`viewport_len`) — just as `list`/`grid` already ask for the visible window. For uniform rows that is `n_rows * row_height`. ## The `on_scroll` callback convention `on_scroll` receives the **delta in px** to add to the offset (not the absolute offset): both the wheel and the scrollbar drag emit deltas, and the caller accumulates and clamps in its `update` with `clamp_offset`. It is the same idea as the `splitter` (the drag handler is reused for the whole drag, so a captured absolute offset would go stale; a delta per event is always correct). ```ignore // view: scroll_y( model.offset, model.rows.len() as f32 * ROW_H, panel_h, list_view, Msg::ScrollBy, // Fn(f32) -> Msg, arg = delta px &ScrollPalette::default(), ) // update: Msg::ScrollBy(d) => { m.offset = clamp_offset(m.offset + d, content_len, viewport_len); } ``` To bring a selection into view (keyboard) see `ensure_visible`; for smooth or inertial scrolling see `approach`. --- Part of **llimphi** — see [llimphi](../../README.md).