Skip to content

Surfaces

ScrollablePane

Bit.BlazorUIScrollViewScrollAreaScrollViewer

ScrollablePane is a box that scrolls whatever is put in it, for content that does not fit in the room the page has for it. The scrolling is the browser's own, so the momentum, the keyboard and the assistive technology of the platform come with it; what the component adds is everything around that. It sizes the box - fixed, full, fit, or growing up to a maximum before it starts scrolling - picks which axes scroll, keeps a scroll from carrying on into the page behind it, reserves the gutter so the layout does not jump, restyles the scrollbars in the colors of the theme and hides them until they are wanted, fades the edges that still have content beyond them, snaps onto the items of a strip one at a time, answers a drag and a wheel where a mouse has no other way of scrolling sideways, and puts the box in the tab order for readers who scroll with the keyboard. It reports where it stands and which way it is going as it moves, brackets a whole scroll with a start and an end, and calls back as each edge is reached, which is what an endless list is built on; it opens where it is told to and can be driven from code with ScrollTo, ScrollBy, ScrollToStart, ScrollToEnd and ScrollToElement; and it holds the reader's place as content arrives at either end - pinned to the bottom of a log that is still being written to, and standing still while a page of older messages loads in above.

Notes

A pane costs nothing on the browser side until it is asked for something only the browser can answer. Sizing, the overflow axes, the gutter, the snapping and the scrollbar styling are all CSS, so such a pane is a single element with a style attribute. The scroll listener, the observers and the round trips appear only once AutoScroll, Fade, DragScroll, HorizontalWheel, PreserveScroll, OnScroll, OnScrollStart, OnScrollEnd or one of the four OnReached* callbacks is used.

Two things here are the browser's to decide. Smooth is motion, so it collapses to an instant move when the OS or the browser reports prefers-reduced-motion: reduce - use the ForceAnimation toggle at the top of this page to see it animate anyway. And the scrollbar itself is drawn by the engine: Modern restyles it fully in Chromium and Safari and thins it to the theme colors in Firefox, while ScrollbarWidth and ScrollbarColor are the standard CSS properties, which Chromium lets win over Modern - so use one or the other on a given pane, not both.

Usage

Every example is live. Open its code to see exactly what produced the component running underneath.

Basic

A pane is a box with a size and some content. Give it a size the content will not fit in - here a Height - and whatever does not fit scrolls, with the scrollbar of the platform and all of its keyboard, wheel and touch handling intact. A pane whose content does fit shows no scrollbar and behaves like the plain box it is.


Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.

Whether it is a tale of adventure, a reflection of truth, or an idea that sparks change, these lines are yours to fill, to shape, and to make uniquely yours. The journey begins here, in this quiet moment where everything is possible.

For now, these lines are here to remind you of the beauty of beginnings. They are the quiet before the symphony, the foundation upon which your creativity will build. Soon, this space will hold your thoughts, your visions, and your voice.

Dimensions

Width and Height take any CSS length. MaxHeight is the one that makes a pane grow with its content and only start scrolling once there is too much of it, which is what a list of results, a set of messages or a menu of unknown length wants - leave the height unset and cap it instead. MinHeight, MinWidth and MaxWidth round out the set.

The flag shortcuts spell out the three sizes that are always wanted: FullHeight / FullWidth / FullSize for 100%, FitHeight / FitWidth / FitSize for fit-content, and AutoHeight / AutoWidth / AutoSize for auto.


Height:

Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.

Whether it is a tale of adventure, a reflection of truth, or an idea that sparks change, these lines are yours to fill, to shape, and to make uniquely yours. The journey begins here, in this quiet moment where everything is possible.

For now, these lines are here to remind you of the beauty of beginnings. They are the quiet before the symphony, the foundation upon which your creativity will build. Soon, this space will hold your thoughts, your visions, and your voice.



Width:

Once upon a time, stories wove connections between people, a symphony of voices crafting shared dreams.


MaxHeight, with a growing number of lines:



Line 1
Line 2

Overflow

Overflow says what happens to content that does not fit, on both axes at once: Auto shows a scrollbar only when there is something to scroll, Scroll always reserves one, Hidden clips the content and scrolls neither way, and Visible lets it spill out of the box. OverflowX and OverflowY say the same thing for one axis and have the last word over it, so a pane that scrolls down but never sideways is OverflowY="BitOverflow.Auto" OverflowX="BitOverflow.Hidden".

NoScroll is the other kind of "no": the content is clipped and neither the wheel, a drag nor the keyboard moves it, but the content stays interactive and the scrolling API of the component still works - which is what a pane whose position is driven entirely from code wants. Disabling the pane with IsEnabled="false" takes the interaction away as well.


Overflow



0
1
2
3
4
5

Horizontal

Horizontal is the shortcut for a single line that scrolls sideways: the content stops wrapping (the pane sets white-space:nowrap, which the content inherits), the vertical axis stops scrolling and the horizontal one takes over. It leaves the layout of the content alone, so a row of block level children still needs a flex or grid container of its own inside the pane - what it settles is the wrapping and the axes, which is the part that is easy to get wrong.

The overflow parameters still have the last word on either axis, so a horizontal pane that should also scroll down can say OverflowY="BitOverflow.Auto".


A long line of text:

Once upon a time, stories wove connections between people, a symphony of voices crafting shared dreams. Each word carried meaning, each pause brought understanding.


A row of items, laid out with a flex container inside the pane:

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12

Overscroll

Overscroll decides what a scroll does once the pane itself has nowhere left to go. Auto lets it carry on into whatever scrolls behind the pane - keep turning the wheel at the bottom of the pane and the page moves instead, which is how a reader loses their place. Contain stops the scroll at the edge of the pane, and None also suppresses the platform's own rubber band and pull to refresh, which is what a pane inside a dialog or a sheet on a touch device wants.

OverscrollX and OverscrollY say the same for one axis. Scroll the pane below to its bottom and keep going: with Contain the outer box stays put.


Overscroll

The outer pane
Inner 1
Inner 2
Inner 3
Inner 4
Inner 5
Inner 6
Inner 7
Inner 8
Inner 9
Inner 10
Inner 11
Inner 12
Outer 1
Outer 2
Outer 3
Outer 4
Outer 5
Outer 6

Scrollbar gutter

A classic scrollbar takes room from the content the moment it appears, so a pane that grows past its height shifts everything in it sideways. Gutter reserves that room in advance: Stable keeps it whether or not there is anything to scroll, and BothEdges mirrors it on the opposite edge so the content stays centered. Auto, the default, reserves nothing.

Add items until the pane starts scrolling and watch the numbers below: with Auto they jump, with the other two they do not. On a platform with overlay scrollbars there is no gutter to reserve and nothing moves either way.


Scrollbar gutter


0
1
2
3
4
5

Scrollbar width & color

ScrollbarWidth asks the engine for a Thin scrollbar, or for None at all - which takes the bar off the screen while leaving the pane scrollable by wheel, touch and keyboard. A hidden scrollbar leaves nothing saying the content carries on, so it is worth pairing with Fade.

ScrollbarColor paints the thumb and the track, in that order and both at once: ScrollbarColor="gray transparent". A single color is ignored by the browser. Both of these are the standard CSS properties, which Chromium lets win over the rendering Modern draws - use one or the other on a given pane.


Thin:

Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.

Whether it is a tale of adventure, a reflection of truth, or an idea that sparks change, these lines are yours to fill, to shape, and to make uniquely yours. The journey begins here, in this quiet moment where everything is possible.

For now, these lines are here to remind you of the beauty of beginnings. They are the quiet before the symphony, the foundation upon which your creativity will build. Soon, this space will hold your thoughts, your visions, and your voice.



None, with a fade so the content still says it carries on:

Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.

Whether it is a tale of adventure, a reflection of truth, or an idea that sparks change, these lines are yours to fill, to shape, and to make uniquely yours. The journey begins here, in this quiet moment where everything is possible.

For now, these lines are here to remind you of the beauty of beginnings. They are the quiet before the symphony, the foundation upon which your creativity will build. Soon, this space will hold your thoughts, your visions, and your voice.



Colored:

Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.

Whether it is a tale of adventure, a reflection of truth, or an idea that sparks change, these lines are yours to fill, to shape, and to make uniquely yours. The journey begins here, in this quiet moment where everything is possible.

For now, these lines are here to remind you of the beauty of beginnings. They are the quiet before the symphony, the foundation upon which your creativity will build. Soon, this space will hold your thoughts, your visions, and your voice.

Modern

Modern takes the scrollbar away from the operating system and gives it to the theme: a thin rounded thumb over a transparent track, with no arrow buttons and no corner, in the foreground colors of the current design system - so it re-skins with the rest of the library and looks the same on every platform.

Chromium and Safari get the full rendering; Firefox, which has no scrollbar pseudo-elements, is given a thin bar in the same colors. The thickness is the --bit-scp-sbs custom property and the three states of the thumb are --bit-scp-sbc, --bit-scp-sbch and --bit-scp-sbca, so a pane can retune any of them from its own Style without leaving the theme behind the way ScrollbarColor would.

AutoHideScrollbar adds the other half of an overlay scrollbar: the thumb is only painted while the pointer is over the pane or something in it holds the focus, so it is there the moment it is wanted and takes no attention while it is not - and never hidden from a reader scrolling with the keyboard. Only the color goes away, so nothing in the pane moves as the bar comes and goes.



Vertical:

Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.

Whether it is a tale of adventure, a reflection of truth, or an idea that sparks change, these lines are yours to fill, to shape, and to make uniquely yours. The journey begins here, in this quiet moment where everything is possible.

For now, these lines are here to remind you of the beauty of beginnings. They are the quiet before the symphony, the foundation upon which your creativity will build. Soon, this space will hold your thoughts, your visions, and your voice.



Horizontal, with a thicker bar:

Once upon a time, stories wove connections between people, a symphony of voices crafting shared dreams. Each word carried meaning, each pause brought understanding.

Fade

An overlay scrollbar disappears when it is not being used, which leaves a pane scrolled to the middle of its content looking exactly like one whose content ends at its edge. Fade fades out each edge that still has something beyond it, and takes the fade off the moment that edge is reached - so the pane always says which way there is more.

All four edges are covered, each one only while the pane can still be scrolled towards it, so a pane that only scrolls up and down never fades its sides. FadeSize sets how far the fade reaches (2rem by default). It is drawn with a mask, so it works over any background, needs no element of its own, and is simply not drawn by a browser that cannot composite masks.

The mask covers the whole pane, so a classic scrollbar and anything held in place inside it with position: sticky fade along with the content behind them. That is what makes the fade worth pairing with a scrollbar that is not on the screen anyway - ScrollbarWidth="BitScrollbarWidth.None", or Modern with AutoHideScrollbar - which is the pairing it is for in the first place.



2

Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.

Whether it is a tale of adventure, a reflection of truth, or an idea that sparks change, these lines are yours to fill, to shape, and to make uniquely yours. The journey begins here, in this quiet moment where everything is possible.

For now, these lines are here to remind you of the beauty of beginnings. They are the quiet before the symphony, the foundation upon which your creativity will build. Soon, this space will hold your thoughts, your visions, and your voice.



Both axes at once:

R1C1
R1C2
R1C3
R1C4
R1C5
R1C6
R1C7
R1C8
R2C1
R2C2
R2C3
R2C4
R2C5
R2C6
R2C7
R2C8
R3C1
R3C2
R3C3
R3C4
R3C5
R3C6
R3C7
R3C8
R4C1
R4C2
R4C3
R4C4
R4C5
R4C6
R4C7
R4C8
R5C1
R5C2
R5C3
R5C4
R5C5
R5C6
R5C7
R5C8
R6C1
R6C2
R6C3
R6C4
R6C5
R6C6
R6C7
R6C8
R7C1
R7C2
R7C3
R7C4
R7C5
R7C6
R7C7
R7C8
R8C1
R8C2
R8C3
R8C4
R8C5
R8C6
R8C7
R8C8

Scroll events

OnScroll reports where the pane stands as it moves, as a BitScrollOffset: the two raw offsets, the size of the content, the size of the visible area, and the answers derived from them - how far along each axis the pane is (PercentX / PercentY), which edges it is standing at, and whether either axis can be scrolled at all. It is what a reading-progress bar, a "scroll to top" button or a synchronized pair of panes is built on.

Each report also carries how far the pane moved to get there - DeltaLeft and DeltaTop, and the ScrollingDown / ScrollingUp / ScrollingRight / ScrollingLeft answers derived from them - so a page that reacts to the direction of a scroll, such as a toolbar that folds away on the way down and comes back on the way up, does not have to keep the previous position of its own.

It reports a position rather than an event, so it also arrives when the position means something new without anything having been scrolled: the pane or its content changing size moves the end of the content and every answer derived from it. Reports are coalesced to at most one per animation frame, and ScrollThrottle spaces them out further, in milliseconds, which is worth setting wherever each report costs a round trip - a Blazor Server circuit most of all. The last report of a scroll always arrives, so the page is never left holding a position the pane has moved on from.

OnScrollStart and OnScrollEnd bracket the whole of a scroll with one call each: the second is the browser's own scrollend - the finger lifted, the scrollbar released, the momentum spent, the animated move finished - with a short idle standing in for it where the browser has no such event. That makes the end of a scroll the right place for the work that is too expensive to do while one is running: saving the reading position, loading what the reader settled on, lighting up the item a carousel came to rest at.




Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12
Row 13
Row 14
Row 15
Row 16
Row 17
Row 18
Row 19
Row 20

Top: 0 of 0  |  PercentY: 0%  |  AtTop: -  |  AtBottom: -
State: -  |  Going: -

Reaching the edges

OnReachedTop, OnReachedBottom, OnReachedLeft and OnReachedRight are called as each edge is reached rather than for as long as the pane stands at it, and are armed again once it has left - so coming to rest at the bottom is one call, not one per frame of getting there. The two horizontal ones are the edges on the screen, so they mean the same thing in a right-to-left pane.

ReachOffset is how near an edge counts as having reached it. An endless list gives it a screenful or so, which is what makes the next page arrive before the reader gets to the bottom rather than after.

Two things are never reported: the edges a pane is already standing at when it is first measured - every pane starts at the top, and a page told about that would fetch what comes before its first item before anything had been scrolled - and either edge of an axis with nothing to scroll, which stands at both at once.



Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12

Last edge reached: -  |  rows: 12

Scrolling the pane from code

A reference to the component gives the whole scrolling API: ScrollToStart and ScrollToEnd for the two ends, ScrollTo for an absolute position (an axis left null stays where it is), ScrollBy for a distance from wherever it stands, ScrollToElement for an element inside the pane, and GetScrollOffset to read the position at a moment of your own choosing. Offsets are given in reading order and clamped to the content, so a right-to-left pane takes the same numbers as any other.

Every one of them takes an optional smooth argument, and the Smooth parameter is the default for all of them - along with fragment navigations into the pane and the browser bringing a focused element inside it into view. ScrollToElement moves the pane and nothing else: scrolling an element into view the browser's own way would scroll every ancestor of the pane too, moving the page under a reader who only asked the pane to move. Left without an offset of its own it keeps whatever room ScrollPadding asks for - the pane below has a sticky header and 2.5rem of it, so "To row 15" leaves the row clear of the header instead of tucking it underneath, exactly as a fragment navigation into the pane would. Its alignment argument says where the element is left: Start (the default), Center, End, or Nearest, which moves the pane as little as it can and not at all for an element that is already in view.

A pane that should open somewhere other than the top - a list you have navigated back to, a conversation you left in the middle of - is InitialScrollTop and InitialScrollLeft: hand back the offset you saved and the pane starts there instead of at the start of its content. They are applied once, on the first render and without animation, so a later change does not move a pane the reader has since scrolled; a pane that should open at the end is AutoScroll instead.

FocusAsync hands the focus to the pane itself, for a pane that Focusable put in the tab order, and Refresh re-measures a pane whose content changed size without changing at all - a web font that finished loading, an image that settled at a size the markup never named - which is the one thing neither of the pane's own observers can see.




A header of the pane's own, and 2.5rem of ScrollPadding under it
Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12
Row 13
Row 14
Row 15
Row 16
Row 17
Row 18
Row 19
Row 20
Row 21
Row 22
Row 23
Row 24
Row 25

-

A pane that opened 250px down, without ever having been at the top:

Row 1
Row 2
Row 3
Row 4
Row 5
Row 6
Row 7
Row 8
Row 9
Row 10
Row 11
Row 12
Row 13
Row 14
Row 15
Row 16
Row 17
Row 18
Row 19
Row 20
Row 21
Row 22
Row 23
Row 24
Row 25

AutoScroll

AutoScroll keeps the pane pinned to the end of its content as the content grows, which is what a chat, a log or a console wants. It pins the pane once as soon as it is turned on, and after that only while the reader left it standing at the end: scroll up to read something and the arriving lines stop dragging you back down, scroll to the bottom again and the pinning takes up where it left off.

AutoScrollThreshold is how near the end still counts as being at it, in pixels - raise it where a line or two of slack should not count as having scrolled away. Smooth animates the moves it makes.





The log starts here.

Preserving the reader's place

A page of older messages arriving at the top of a conversation is as tall as the messages in it, and it pushes everything the reader was in the middle of reading that far down the screen. PreserveScroll moves the pane down by exactly what arrived, so what they were looking at does not move at all.

Only content that lands above the visible area counts, so an arrival at the bottom is left alone and a pane standing at the very top of its content - which has no place to keep - is too. That is what makes it safe to pair with OnReachedTop: give ReachOffset a screenful so the fetch starts before the reader is at the top rather than once they are.

Every engine but WebKit already does this on its own (it is the CSS overflow-anchor behavior), so the parameter changes nothing where the browser has it and brings the rest - Safari, most of all - up to the same behavior. Which is why the pane below sets overflow-anchor: none on itself, through a class of the page: that turns the browser's own anchoring off, so every engine behaves like WebKit here and the toggle has something to show wherever you are reading this. A pane of your own needs none of that - leave the anchoring alone and PreserveScroll steps in only on the engines that lack it.

Scroll to the top of the pane below to load the messages before it, with the toggle on and then off. With it on, the message you were reading stays where it is; with it off, eight messages arrive above it and push it down the screen.



Message 1
Message 2
Message 3
Message 4
Message 5
Message 6
Message 7
Message 8
Message 9
Message 10
Message 11
Message 12
Message 13
Message 14

Oldest message loaded: 1  |  messages: 14

Keyboard & accessibility

A scrolling box that holds nothing focusable cannot be reached with a keyboard: there is nothing inside it to tab to, and the arrow keys only scroll what has the focus. Focusable puts the box itself in the tab order, which is what WCAG 2.1.1 asks of a scrolling region and what the "scrollable region must have keyboard access" rule checks for. Once it has the focus, the arrow keys, Page Up/Down, Home and End all scroll it.

Turn it on for a pane of plain content - text, a table, an image - and leave it off for one that already holds links, buttons or fields, where it would only add a tab stop in front of them. A pane that is in the tab order is worth naming with AriaLabel and marking with a Role of region or group, so what took the focus is announced as something rather than as an unnamed stop. Setting TabIndex makes the pane focusable on its own, and a disabled pane is taken back out of the tab order.



Tab into the pane below and scroll it with the arrow keys:

Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.

Whether it is a tale of adventure, a reflection of truth, or an idea that sparks change, these lines are yours to fill, to shape, and to make uniquely yours. The journey begins here, in this quiet moment where everything is possible.

For now, these lines are here to remind you of the beauty of beginnings. They are the quiet before the symphony, the foundation upon which your creativity will build. Soon, this space will hold your thoughts, your visions, and your voice.

Snapping

Snap makes the pane come to rest on the snap positions of its content instead of anywhere between them, which is how the platform itself builds a carousel, a set of pages or a strip that always stops on an item: the scrolling stays the browser's, with all of its momentum, wheel and keyboard handling, and only where it is allowed to stop is decided here. Mandatory always ends on an item, Proximity only when the scroll came to rest near one, and None turns it back off.

The positions themselves belong to the items, and SnapAlign is what puts them there - Start, Center or End, applied to the direct children of the pane. A pane whose items sit inside a layout container of their own (the flex row a horizontal strip is usually laid out with) has that container as its only child, so give the items a scroll-snap-align of your own there instead. A pane that is also moved from code is worth keeping to Proximity: a mandatory snap pulls such a move onto the nearest item rather than leaving it where it was put.

SnapStop is what turns a snapping strip into a carousel: without it a flick comes to rest on whichever item the momentum happened to run out over, several along; with it the pane stops at the first position it reaches, however hard the flick, the wheel or the page key that started the scroll. Flick the strip below hard with each setting to see it.


Snap

SnapAlign


Card 1
Card 2
Card 3
Card 4
Card 5
Card 6
Card 7
Card 8
Card 9
Card 10

Dragging & the wheel

A mouse has no good way of scrolling a strip sideways: its wheel only has one axis, and the scrollbar of the strip is a long way from what the reader is looking at. DragScroll answers that the way every map and canvas on the web does - hold the pointer down and pull the content along - and HorizontalWheel answers it by taking the vertical wheel to mean the only axis such a pane has.

Both are deliberately narrow. A drag only starts once the pointer has actually travelled, so a click inside the pane is still a click, and the click that ends a real drag is swallowed rather than delivered to whatever card it stopped over; drags that start on a field, a link or a button are left alone, as is anything marked data-bit-scp-nodrag; and a touch is left to the browser, which drags already. The wheel only acts while the pane has somewhere to go sideways and nowhere to go up or down, leaves a trackpad's own horizontal delta alone, and hands the scroll back to the page as soon as the pane reaches the end it is being pushed towards - so the page under the pointer is never left stuck.

DragMomentum is what makes a released drag carry on at the speed it was let go at and slow to a stop, the way a flick does on every scrolling surface of every platform - without it, crossing a long strip is a row of separate drags rather than one throw of it. Only a release that was actually moving counts, so a pointer held still before it was lifted was never a flick; a new drag stops the glide where it stands; and a pane whose Snap is Mandatory is left to come to rest on an item its own way instead.



Card 1
Card 2
Card 3
Card 4
Card 5
Card 6
Card 7
Card 8
Card 9
Card 10

Style & Class

The pane is a single element, so Style and Class land straight on the box that scrolls. Either is also where the custom properties of the component are retuned: --bit-scp-sbs for the thickness of the Modern scrollbar, --bit-scp-sbc / --bit-scp-sbch / --bit-scp-sbca for its thumb at rest, under the pointer and while it is being dragged, and --bit-scp-fsz for how far the Fade reaches, which FadeSize sets for you.


Component's Style:

Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.



Component's Class:

Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. These placeholder words symbolize the beginning - a moment of possibility where creativity has yet to take shape. Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and inspirations will be built.

In the beginning, there is silence, a blank canvas yearning to be filled, a quiet space where creativity waits to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the vibrant narratives of tomorrow.

In this space, potential reigns supreme. It is a moment suspended in time, where imagination dances freely and each word has the power to transform into something extraordinary. Here lies the start of something new, an opportunity to craft, inspire, and create.

RTL

Setting Dir to BitDir.Rtl lays the pane out right to left, and each part of the API keeps meaning what its name says. The ones named after the content follow the content: ScrollToStart, ScrollToEnd and AutoScroll all treat the visual left edge of a right-to-left pane as the end of it, and ScrollToElement with an alignment of Start brings an item to the right hand edge. The ones named after the screen follow the screen: OnReachedLeft is the left hand edge either way, and ScrollTo and ScrollBy count from and towards the visual left, which is how BitScrollOffset reports a position - so what you read off a pane can be handed straight back to it.

A right-to-left pane counts its raw Left down from 0 at the right edge into the negatives, which is why BitScrollOffset carries Rtl along with the position and derives OffsetLeft, PercentX, AtLeft and AtRight from both: they all mean the same thing on the screen whichever way the pane reads.


داستان‌ها روزگاری پیوند میان مردم را می‌بافتند، سمفونی‌ای از صداها که رویاهای مشترک را می‌ساخت. هر واژه معنایی داشت و هر مکث فهمی به همراه می‌آورد.

در آغاز، سکوت است؛ بومی سفید که در انتظار پر شدن است، فضایی آرام که در آن خلاقیت منتظر بیدار شدن است. این واژه‌ها موقتی‌اند و جای ایده‌هایی را گرفته‌اند که هنوز نیامده‌اند.

در این فضا، امکان حکمرانی می‌کند. لحظه‌ای معلق در زمان، جایی که تخیل آزادانه می‌رقصد و هر واژه توان آن را دارد که به چیزی خارق‌العاده بدل شود.

هر داستانی با بومی سفید آغاز می‌شود؛ فضایی آرام که منتظر پر شدن با ایده‌ها، احساس‌ها و رویاهاست. این واژه‌های موقت نشانهٔ آغازند؛ لحظه‌ای از امکان که هنوز خلاقیت در آن شکل نگرفته است.

فعلاً این سطرها اینجا هستند تا زیبایی آغازها را به یاد بیاورند. آن‌ها سکوت پیش از سمفونی‌اند، بنیادی که خلاقیت شما بر آن ساخته خواهد شد.



A horizontal right-to-left pane:

داستان‌ها روزگاری پیوند میان مردم را می‌بافتند، سمفونی‌ای از صداها که رویاهای مشترک را می‌ساخت.

API

Every parameter, public member, sub-class and enum this component exposes.

BitScrollablePane parameters

Name Type Default value Description
AutoScroll bool false Keeps the pane pinned to the end of its content as the content grows. It pins once as soon as it is turned on, and after that only while the reader left the pane standing at the end.
AutoScrollThreshold int 0 How near the end of the content (in pixels) the pane has to have been left for AutoScroll to keep pinning it there.
AutoHeight bool false Makes the height of the pane auto.
AutoSize bool false Makes both height and width of the pane auto.
AutoWidth bool false Makes the width of the pane auto.
AutoHideScrollbar bool false Keeps the Modern scrollbar of the pane out of sight until the pointer is over it or something in it holds the focus.
Body RenderFragment? null Alias for the ChildContent parameter.
ChildContent RenderFragment? null The content of the pane, it can be any custom tag or text.
DragMomentum bool false Lets a released drag carry on at the speed it was let go at and slow to a stop, instead of stopping dead with the button. It only applies to DragScroll.
DragScroll bool false Lets the pane be scrolled by dragging its content with a mouse or a pen, which is how a strip that only scrolls sideways is reached without a horizontal wheel.
Fade bool false Fades out each edge of the pane that still has content beyond it, and takes the fade off the moment that edge is reached.
FadeSize string? null How far the Fade reaches into the pane, as any CSS length. It defaults to 2rem.
FitHeight bool false Makes the height of the pane fit-content.
FitSize bool false Makes both height and width of the pane fit-content.
FitWidth bool false Makes the width of the pane fit-content.
Focusable bool false Puts the pane itself in the tab order, so a pane holding nothing focusable can still be scrolled with the keyboard.
FullHeight bool false Makes the height of the pane 100%.
FullSize bool false Makes both height and width of the pane 100%.
FullWidth bool false Makes the width of the pane 100%.
Gutter BitScrollbarGutter? null Allows to reserve space for the scrollbar, preventing unwanted layout changes as the content grows while also avoiding unnecessary visuals when scrolling isn't needed.
Height string? null The height of the pane.
Horizontal bool false Lays the content out on a single line that scrolls sideways instead of wrapping, and turns the vertical scrolling off.
HorizontalWheel bool false Turns a vertical wheel over a pane that only scrolls sideways into a sideways scroll, and hands the scroll back to the page once the pane reaches that end.
InitialScrollLeft double? null Where the pane stands the first time it is rendered, measured from the visual left edge of the content in pixels. It is applied once and never animated.
InitialScrollTop double? null Where the pane stands the first time it is rendered, measured from the top of the content in pixels. It is applied once and never animated.
MaxHeight string? null The maximum height of the pane, which is what makes it grow with its content and only start scrolling once there is too much of it.
MaxWidth string? null The maximum width of the pane.
MinHeight string? null The minimum height of the pane.
MinWidth string? null The minimum width of the pane.
Modern bool false Enables a modern style for the scrollbar of the pane, drawn in the colors of the theme instead of by the operating system.
NoScroll bool false Turns the scrolling of the pane off while leaving its content interactive and the scrolling API of the component working.
OnReachedBottom EventCallback Callback for when the pane has been scrolled to the bottom of its content.
OnReachedLeft EventCallback Callback for when the pane has been scrolled to the visual left edge of its content.
OnReachedRight EventCallback Callback for when the pane has been scrolled to the visual right edge of its content.
OnReachedTop EventCallback Callback for when the pane has been scrolled to the top of its content.
OnScroll EventCallback<BitScrollOffset> Callback for when the pane is scrolled, carrying where it now stands.
OnScrollEnd EventCallback<BitScrollOffset> Callback for when the pane has come to rest after being scrolled, carrying where it stopped.
OnScrollStart EventCallback<BitScrollOffset> Callback for when the pane starts being scrolled, carrying where it stood as it set off.
Overflow BitOverflow? null Controls the visibility of scrollbars in the pane.
OverflowX BitOverflow? null Controls the visibility of X-axis scrollbar in the pane. It has the last word on that axis, over both Overflow and Horizontal.
OverflowY BitOverflow? null Controls the visibility of Y-axis scrollbar in the pane. It has the last word on that axis, over both Overflow and Horizontal.
Overscroll BitOverscroll? null What the browser does with a scroll that has already reached the edge of the pane.
OverscrollX BitOverscroll? null What the browser does with a horizontal scroll that has already reached the edge of the pane.
OverscrollY BitOverscroll? null What the browser does with a vertical scroll that has already reached the edge of the pane.
PreserveScroll bool false Keeps the reader's place when content is added above what they are looking at, which is what a conversation that loads older messages at its top needs. Every engine but WebKit already does this on its own, so it only steps in where the browser is not anchoring the pane - which a pane setting overflow-anchor: none on itself is not, on any engine.
ReachOffset int 0 How near an edge (in pixels) counts as having reached it, for the four edge callbacks.
Role string? null The ARIA role of the pane. A pane renders none of its own; set it to region or group, along with AriaLabel, where the pane is a part of the page in its own right.
ScrollbarColor string? null Sets the color of the scrollbar thumb and track, in that order. For specific colors, it has to contain both colors separated by a space or otherwise it won't work.
ScrollbarWidth BitScrollbarWidth? null Sets the desired thickness of scrollbars when they are shown.
ScrollPadding string? null The inset the pane keeps between its edges and anything scrolled into view inside it, as any CSS length.
ScrollThrottle int 0 The shortest interval (in milliseconds) between two OnScroll reports. Reports are always coalesced to at most one per animation frame.
Smooth bool false Animates every move the pane does not make by hand: the scrolling API, a fragment navigation into it, and the pinning AutoScroll does.
Snap BitScrollSnap? null Makes the pane come to rest on the snap positions of its content instead of anywhere between them.
SnapAlign BitScrollSnapAlign? null Where the direct children of the pane come to rest in it while Snap is on.
SnapStop bool false Keeps a fast scroll from passing over the snap positions it goes by, which is what turns a snapping strip into a carousel that moves one item per gesture.
Width string? null The width of the pane.

BitScrollablePane public members

Name Type Default value Description
FocusAsync ValueTask FocusAsync() / ValueTask FocusAsync(bool preventScroll) Gives the focus to the pane itself, which only takes it while Focusable or a TabIndex has put it in the tab order.
GetScrollOffset ValueTask<BitScrollOffset?> Reads where the pane currently stands, straight from the browser.
Refresh ValueTask Re-measures the pane and brings the fade and the edge callbacks back up to date, for the size changes neither of its observers can see.
ScrollBy ValueTask ScrollBy(double x, double y, bool? smooth = null) Scrolls the pane by a distance from wherever it currently stands, in pixels and measured on the screen: a positive x always moves the pane rightwards.
ScrollTo ValueTask ScrollTo(double? left, double? top, bool? smooth = null) Scrolls the pane to an absolute position, in pixels from the top and from the visual left edge of the content. An axis that is left null stays where it is.
ScrollToElement ValueTask ScrollToElement(string elementId, double offset = 0, bool? smooth = null, BitScrollAlignment alignment = BitScrollAlignment.Start) Brings an element inside the pane into view by scrolling the pane itself, leaving every scrolling ancestor of it alone.
ScrollToEnd ValueTask ScrollToEnd(bool? smooth = null) Scrolls the pane to the end of its content, both horizontally and vertically, which on the horizontal axis is the visual left edge of a right-to-left pane.
ScrollToStart ValueTask ScrollToStart(bool? smooth = null) Scrolls the pane back to the start of its content, both horizontally and vertically, which on the horizontal axis is the visual right edge of a right-to-left pane.

BitComponentBase parameters

Name Type Default value Description
AriaLabel string? null Gets or sets the accessible label for the component, used by assistive technologies.
Class string? null Gets or sets the CSS class name(s) to apply to the rendered element.
Dir BitDir? null Gets or sets the text directionality for the component's content.
ForceAnimation bool false Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested.
HtmlAttributes Dictionary<string, object> new Dictionary<string, object>() Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters.
Id string? null Gets or sets the unique identifier for the component's root element.
IsEnabled bool true Gets or sets a value indicating whether the component is enabled and can respond to user interaction.
Style string? null Gets or sets the CSS style string to apply to the rendered element.
TabIndex string? null Gets or sets the tab order index for the component when navigating with the keyboard.
Visibility BitVisibility BitVisibility.Visible Gets or sets the visibility state (visible, hidden, or collapsed) of the component.

BitComponentBase public members

Name Type Default value Description
UniqueId Guid Guid.NewGuid() Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed.
RootElement ElementReference Gets the reference to the root HTML element associated with this component.

BitScrollOffset properties

Where a pane stands, as measured in the browser. Everything is in CSS pixels.

Name Type Default value Description
Left double 0 The raw scrollLeft of the pane, so in a right-to-left pane it starts at 0 at the right edge and runs negative.
Top double 0 How far the content has been scrolled down.
ScrollWidth double 0 The full width of the content, including the part of it that is scrolled out of sight.
ScrollHeight double 0 The full height of the content, including the part of it that is scrolled out of sight.
ClientWidth double 0 The width of the visible area of the pane, without its scrollbar.
ClientHeight double 0 The height of the visible area of the pane, without its scrollbar.
Rtl bool false Whether the pane was laid out right to left when it was measured, which is what tells the two readings of a Left of 0 apart.
DeltaLeft double 0 How far the pane moved sideways since the position before this one was reported, positive rightwards on the screen. Only the reports OnScroll makes carry it.
DeltaTop double 0 How far the pane moved up or down since the position before this one was reported, positive downwards. Only the reports OnScroll makes carry it.
OffsetLeft double 0 The distance from the visual left edge of the content, which is Left made positive and direction independent.
MaxLeft double 0 The largest horizontal offset the pane can reach.
MaxTop double 0 The largest vertical offset the pane can reach.
ScrollableX bool false Whether the content is wider than the pane.
ScrollableY bool false Whether the content is taller than the pane.
AtLeft bool false Whether the pane is standing at the visual left edge of its content.
AtRight bool false Whether the pane is standing at the visual right edge of its content.
AtTop bool false Whether the pane is standing at the top of its content.
AtBottom bool false Whether the pane is standing at the bottom of its content.
PercentX double 0 How far the pane has been scrolled sideways, from 0 at the visual left edge to 1 at the right one.
PercentY double 0 How far the pane has been scrolled down, from 0 at the top to 1 at the bottom.
ScrollingDown bool false Whether the move this report carries was downwards, which is what a header that folds away on the way down reads.
ScrollingUp bool false Whether the move this report carries was upwards.
ScrollingRight bool false Whether the move this report carries was to the right on the screen, whichever way the pane reads.
ScrollingLeft bool false Whether the move this report carries was to the left on the screen, whichever way the pane reads.

BitScrollAlignment enum

Name Value Description
Start 0 The element is brought to the start of the pane: its top edge to the top of the pane, and its leading edge to the leading edge of the pane.
Center 1 The element is centered in the pane along both axes.
End 2 The element is brought to the end of the pane: its bottom edge to the bottom of the pane, and its trailing edge to the trailing edge of the pane.
Nearest 3 The pane moves as little as it can: an element that is already fully in view is not moved to at all, and one that is not is brought to whichever edge it is nearest.

BitScrollSnap enum

Name Value Description
None 0 The pane does not snap, which is the initial value.
Proximity 1 The pane snaps to a position only when it comes to rest near one, so a scroll can still be left anywhere between two items.
Mandatory 2 The pane always comes to rest on a snap position, which is what a carousel or a row of pages wants.

BitScrollSnapAlign enum

Name Value Description
None 0 The children carry no snap position of their own.
Start 1 Each child comes to rest at the start of the pane.
Center 2 Each child comes to rest in the middle of the pane.
End 3 Each child comes to rest at the end of the pane.

BitOverflow enum

Name Value Description
Auto 0 Scrollbars are displayed automatically when needed based on the content size, and hidden when not needed.
Hidden 1 Scrollbars are always hidden, even if the content overflows the visible area.
Scroll 2 Scrollbars are always visible, allowing users to scroll through the content even if it doesn't overflow the visible area.
Visible 3 Overflow content is not clipped and may be visible outside the element's padding box.

BitOverscroll enum

Name Value Description
Auto 0 The initial value: the scroll carries on into the nearest scrolling ancestor once the pane has reached its edge, and the platform's own overscroll affordance is kept.
Contain 1 The scroll stops at the edge of the pane instead of carrying on into the page behind it, while the platform's own overscroll affordance inside the pane is kept.
None 2 Like Contain, and the platform's own overscroll affordance is suppressed as well, so the pane neither bounces nor triggers a pull to refresh at its edges.

BitScrollbarGutter enum

Name Value Description
Auto 0 The initial value. Classic scrollbars create a gutter when overflow is scroll, or when overflow is auto and the box is overflowing. Overlay scrollbars do not consume space.
Stable 1 When using classic scrollbars, the gutter will be present if overflow is auto, scroll, or hidden even if the box is not overflowing. When using overlay scrollbars, the gutter will not be present.
BothEdges 2 If a gutter would be present on one of the inline start/end edges of the box, another will be present on the opposite edge as well.

BitScrollbarWidth enum

Name Value Description
Auto 0 The default scrollbar width for the platform.
Thin 1 A thin scrollbar width variant on platforms that provide that option, or a thinner scrollbar than the default platform scrollbar width.
None 2 No scrollbar shown, however the element will still be scrollable.

BitVisibility enum

Name Value Description
Visible 0 The content of the component is visible.
Hidden 1 The content of the component is hidden, but the space it takes on the page remains (visibility:hidden).
Collapsed 2 The component is hidden (display:none).

BitDir enum

Name Value Description
Ltr 0 Ltr (left to right) is to be used for languages that are written from the left to the right (like English).
Rtl 1 Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic).
Auto 2 Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.