Skip to content

PlBackTop

The way back to the top of a long page. It stays hidden until the page has scrolled far enough to need it, which is the whole design.

React
tsx
import { PlBackTop } from 'plass-ui';

<PlBackTop />;
dart
import 'package:plass_ui/plass_ui.dart';

Stack(
  children: <Widget>[
    ListView(controller: controller, children: rows),
    Positioned(right: 24, bottom: 24, child: PlBackTop(controller: controller)),
  ],
);

Props

PropTypeDefaultDescription
targetWindow | HTMLElement | RefObject<HTMLElement | null> | (() => Window | HTMLElement | null)windowWhat is scrolled. A ref for a panel that scrolls inside the page
visibilityHeightnumber400How far down the reader has to be before it appears, in pixels. Roughly one screen on a laptop
labelstring'Back to top'What it does, in words, and its accessible name
iconReactNodeThe glyph. An upward chevron by default
floatingbooleantrueWhether it pins itself to the bottom corner of the window
variantshared'solid' | 'glass' | 'ghost''glass'What the surface is made of
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'The size of the disc
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'Semantic colour role
elevationshared0 | 1 | 2 | 32Drop shadow depth
PropTypeDefaultDescription
controllerScrollController?PrimaryScrollControllerWhat is scrolled, and what is watched. Left out, the PrimaryScrollController — which is what a ListView with no controller of its own is attached to, and is therefore the equivalent of "the window"
visibilityHeightdouble400How far down the reader has to be before it appears, in pixels. Roughly one screen on a laptop
labelString'Back to top'What it does, in words, and its accessible name
iconWidget?The glyph. An upward chevron by default
onPressedVoidCallback?Runs instead of the scroll. For the screen whose "up" is somewhere other than offset zero
variantsharedPlassVariantPlassVariant.glassWhat the surface is made of
sizesharedPlassSizePlassSize.mdThe size of the disc
colorsharedPlassColorPlassColor.primarySemantic colour role
elevationsharedint2Drop shadow depth

Every native <button> attribute passes straight through, and everything else is a PlIconButton's, the three materials, the elevation ladder, the pointer light.

There is no floating, and no equivalent of it. Flutter has no position: fixed, so where the button goes is the caller's: a Stack over the scrollable with a Positioned or an Align in it, which is how a Flutter screen pins anything to a corner.

controller is the ScrollController, left out, the PrimaryScrollController, which is what a ListView with no controller of its own attaches to and is therefore this framework's "the window". onPressed runs instead of the scroll rather than before it, which is the shape a Dart caller wants: there is no event to preventDefault.

Hidden until it is useful

A button pinned to the corner of every page from the first paint is one more thing covering the content, and on a page short enough not to scroll it is a control that does nothing.

It appears when the reader is visibilityHeight pixels down, 400 by default, roughly one screen on a laptop, which is the point at which scrolling back stops being something they would just do.

While it is out of reach it is aria-hidden and out of the tab order, not merely faded. A control a reader can tab to and cannot see is worse than one that is not there.

Examples

target

The window by default. A ref or an element for a panel that scrolls inside the page, a table's scroll box, a chat log, a modal's body.

tsx
const panel = useRef<HTMLDivElement>(null);

<div ref={panel} className="overflow-y-auto">

</div>
<PlBackTop target={panel} />

floating

On by default, because that is what this component is. Turn it off to put the button somewhere of your own (the end of an article, a toolbar), and keep the appearing and the scrolling.

tsx
<PlBackTop floating={false} className="mx-auto mt-8" />

The glyph and the words

tsx
<PlBackTop icon={<ArrowUpIcon />} label="위로" />

label is the accessible name and what the tooltip a browser draws says. Name it for what pressing it does.

Notes

  • The scroll is smooth, and not under prefers-reduced-motion, a page that flies past a reader who asked for less movement is the exact case that setting exists for. It jumps instead, which arrives at the same place.
  • The position is read once on mount as well as on every scroll, so a page restored halfway down (a back navigation, an anchor in the URL) has the button already there.
  • A caller's own onClick runs first, and calling preventDefault() in it stops the scroll. That is how to take the reader somewhere other than the top.

Released under the MIT License