Skip to content

HorizontalScroll

A full-viewport horizontally scrolling shell whose children flow as panels. The mouse wheel scrolls it sideways by default, and hands control back to the page at either end. Pair it with Parallax bands set to axis="x" for layers that drift sideways as the shell scrolls.

Import

import { HorizontalScroll } from "@hyzer-labs/ui"

Demo

Every demo below sets --hz-horizontal-scroll-height to a small height with a page class, so it scrolls in place instead of taking over the viewport the way a real full-page shell would. There's no separate demo box here: what you're looking at is the component, at any height you give it.

The scrollbar stays on purpose. At rest it is the only sign that the shell scrolls sideways, and it gives a pointer path to anyone who cannot drag or use a wheel. There's no prop to hide it, but your own scrollbar-width: thin will slim it. If a shell like this is your page's main content, give it role="region" and a label, or use as="main": a plain scroll region only needs to be a tab stop for keyboard access, but a landmark needs both a role and a name. A panel that needs to scroll vertically should be its own scroller, since this shell only ever moves sideways.

A panel is any direct child. There's no Panel subcomponent and no wrapper element. wheel is on by default, so a plain mouse wheel already moves the shell sideways with nothing switched on. Try it below, along with the scrollbar, a two-finger trackpad pan, a touch swipe, or tabbing in and pressing an arrow key. Then keep wheeling once you reach the last panel: the shell hands the wheel back, and this page keeps scrolling underneath you.

Panel 1

Panel 2

Panel 3

Panel 4

<HorizontalScroll>
	<section>Panel one</section>
	<section>Panel two</section>
	<section>Panel three</section>
	<section>Panel four</section>
</HorizontalScroll>

<!-- wheel is on by default: a plain mouse wheel already moves this
     sideways, and it hands off to the page at either end -->

Props

NameTypeDefaultNote
asstring'div'Rendered via <svelte:element>. 'section' and 'main' are common choices.
snapbooleanfalseCSS scroll-snap at panel starts. Off by default, because panels are viewport-sized and meant to be read while they move, unlike a rail of small cards.
wheelbooleantrueTurns a plain, vertical-dominant wheel notch into horizontal travel. On by default, since it is most of what makes the shell feel right under a mouse. It defers to a nested vertical scroller, and hands the wheel back to the page at either end. Set wheel={false} for native-only scrolling (touch, trackpad, scrollbar, keyboard).
childrenSnippet—The panels, as direct children. There is no Panel subcomponent: any element you write directly inside is a panel.
classstring—Merged after the hz-horizontal-scroll class.

Anything not listed above is forwarded as an attribute to the root element (or the native control in form components). So id, data-*, aria-*, and event handlers just work.

Theme hooks

What this component promises your CSS. The reference theme styles exactly these — from @layer hz-theme, so your unlayered rules win. See Styling Components for the how.

Root class: .hz-horizontal-scroll

Data attributes

HookValuesStyles
data-snappresent when snapping is onDrives scroll-snap-type: x mandatory on the root. Absent by default — free continuous scrolling.
data-wheelpresent once the wheel remap is actually listening (client-side only)Reflects the attached listener, not the wheel prop — absent server-side and pre-hydration, and absent whenever wheel is false.
data-wheelingpresent while a remapped wheel burst is in flightSuppresses scroll-snap-type for the duration so a mandatory snap does not fight a live scrollLeft assignment; clears ~150ms after the last consumed event, and the browser settles to the nearest panel on its own. Never present when wheel is false.

Custom properties

HookValuesStyles
--hz-horizontal-scroll-height<length> — default 100dvhThe shell block-size. dvh survives a mobile URL bar collapsing; override for an embedded (non-full-viewport) shell.
--hz-horizontal-scroll-panel-width<length | percentage> — default 100%One knob for how much of the shell a panel fills. auto sizes each panel to its content.
--hz-horizontal-scroll-gap<length> — default 0Gap between panels.

Accessibility

The shell is a keyboard tab stop with native arrow-key scrolling. Home and End jump to the first or last panel, instantly when the visitor asks for less motion. Every panel is in the normal tab order, in DOM order, and tabbing to an off-screen panel scrolls it into view on its own. The component adds no role and no accessible name, because a focusable scroll region needs neither for keyboard access. If this shell is your page's main content, give it role="region" aria-label="…" yourself, or use as="main".

The wheel remap is on by default. It only ever takes plain, unmodified, vertical-dominant wheel input that the shell can use. It never takes pinch-zoom, shift+wheel, a trackpad pan, touch, the scrollbar, or the keyboard, and it stops at either end, where scrolling goes straight back to the page. Set wheel={false} for native-only scrolling. No setting leaves a visitor unable to scroll away.