GUILD OF GLEKS UIv21.4.4

gog-scroll

Scroll

A drop-in replacement for a native overflow: auto region. Content keeps scrolling natively — wheel, touch, keyboard, focus-into-view all still work — only the browser's own scrollbar chrome is hidden and replaced with a themeable, draggable overlay thumb.

Overview

Import the component and give it a height (or width).

typescript
import { ScrollComponent } from '@guildofgleks/ui';

@Component({
  // ...
  imports: [ScrollComponent],
})

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

Row 26

Row 27

Row 28

Row 29

Row 30

<gog-scroll class="scroll-box" ariaLabel="Example list">
  @for (item of items; track item) {
    <p class="scroll-row">{{ item }}</p>
  }
</gog-scroll>

Examples

Axis

vertical (default), horizontal, or both. Native scrolling on a disabled axis is left completely alone.

Column 1Column 2Column 3Column 4Column 5Column 6Column 7Column 8Column 9Column 10Column 11Column 12Column 13Column 14Column 15Column 16Column 17Column 18Column 19Column 20
<gog-scroll axis="horizontal" class="scroll-box" ariaLabel="Example columns">
  <div class="scroll-row-flex">
    @for (item of items; track item) {
      <span class="scroll-chip">{{ item }}</span>
    }
  </div>
</gog-scroll>

Size and auto-hide

size="thin" shrinks the track/thumb; autoHide="false" keeps the thumb always visible instead of fading out after hideDelay ms. [showTrack]="false"21.3.1 removes the visual affordance altogether — scrolling itself (wheel, touch, keyboard) is unaffected either way, which is what makes it safe for a region whose scrolling is driven for the user, the way gog-tabs drives its header row. Settable app-wide via GOG_CONFIG.scroll.showTrack.

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

Row 26

Row 27

Row 28

Row 29

Row 30

<div class="controls">
  <gog-button size="sm" (gogClick)="size.set(size() === 'normal' ? 'thin' : 'normal')">
    Size: {{ size() }}
  </gog-button>
  <gog-button size="sm" (gogClick)="autoHide.set(!autoHide())">
    Auto-hide: {{ autoHide() ? 'on' : 'off' }}
  </gog-button>
</div>

<gog-scroll [size]="size()" [autoHide]="autoHide()" class="scroll-box" ariaLabel="Example list">
  @for (item of items; track item) {
    <p class="scroll-row">{{ item }}</p>
  }
</gog-scroll>

Reach events

gogReachStart / gogReachEnd fire once per crossing; gogScroll emits live metrics on every scroll/resize.

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

Row 26

Row 27

Row 28

Row 29

Row 30

Scroll to the edges to see gogReachStart / gogReachEnd fire.

<gog-scroll
  class="scroll-box"
  ariaLabel="Example list"
  [reachThreshold]="8"
  (gogReachStart)="reachState.set('At the top')"
  (gogReachEnd)="reachState.set('At the bottom')"
  (gogScroll)="onScroll($event)"
>
  @for (item of items; track item) {
    <p class="scroll-row">{{ item }}</p>
  }
</gog-scroll>

<p class="status">
  {{ reachState() }}
  @if (lastMetrics(); as metrics) {
    — scrollTop: {{ metrics.scrollTop | number: '1.0-0' }}
  }
</p>

Scroll methods

Grab the instance with viewChild and call scrollToTop / scrollToBottom / scrollToLeft / scrollToRight / scrollTo directly.

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

Row 26

Row 27

Row 28

Row 29

Row 30

<div class="controls">
  <gog-button size="sm" (gogClick)="scroller()?.scrollToTop()">To top</gog-button>
  <gog-button size="sm" (gogClick)="scroller()?.scrollToBottom()">To bottom</gog-button>
</div>

<!-- The reference is `scrollRegion`, not `scroller`: a template reference variable shadows a
     class member of the same name, so `scroller()` would resolve to the component instance
     rather than to the viewChild signal, and the call would not compile. -->
<gog-scroll #scrollRegion class="scroll-box" ariaLabel="Example list">
  @for (item of items; track item) {
    <p class="scroll-row">{{ item }}</p>
  }
</gog-scroll>

Global defaults with GOG_CONFIG

size, autoHide, hideDelay and overscrollBehavior default to undefined so they can fall through to an app-wide (or subtree-wide) default set once via provideGogConfig(...), instead of repeating the same inputs on every instance. An instance's own input, when set, always wins.

This example has no template — it is configuration, not markup.

API Reference

Inputs

NameTypeDefaultDescription
axis'vertical' | 'horizontal' | 'both''vertical'Which axes get an overlay thumb. Native scrolling on the other axis is unaffected.
size'normal' | 'thin' | undefinedundefinedUnset, falls back to GOG_CONFIG.scroll.size, then to 'normal'.
autoHideboolean | undefinedundefinedFades the thumb out after hideDelay ms of inactivity; false keeps it always visible. Unset, falls back to GOG_CONFIG.scroll.autoHide, then to true.
hideDelaynumber | undefinedundefinedUnset, falls back to GOG_CONFIG.scroll.hideDelay, then to 800.
reachThresholdnumber0Pixel distance from an edge that still counts as "reached" for gogReachStart/gogReachEnd.
focusablebooleantrueRenders the viewport as its own tab stop (tabindex="0", role="region"). Turn off when nesting inside a component that already owns focus/keyboard handling.
ariaLabelstring''Accessible name for the viewport when focusable is true and there is no visible label.
showTrack21.3.1boolean | undefinedundefinedWhether the overlay thumb and track render at all. Scrolling itself is unaffected either way — this is purely the visual affordance. Unset, falls back to GOG_CONFIG.scroll.showTrack, then to true.
overscrollBehavior'auto' | 'contain' | 'none' | undefinedundefinedWhat happens when a scroll gesture reaches this instance's edge. Unset, falls back to GOG_CONFIG.scroll.overscrollBehavior, then to 'auto' — chains to the next scrollable ancestor.

Outputs

NamePayloadDescription
gogScrollGogScrollMetricsEmits scrollTop/scrollLeft/scrollHeight/scrollWidth/clientHeight/clientWidth on every scroll/resize.
gogReachStart'vertical' | 'horizontal'Fires once when that axis crosses back within reachThreshold of its start edge.
gogReachEnd'vertical' | 'horizontal'Fires once when that axis crosses within reachThreshold of its end edge.

Methods

NameDescription
scrollTo(options: ScrollToOptions)Forwards to the viewport's native Element.scrollTo.
scrollToTop(behavior = 'smooth')Scrolls to the top.
scrollToBottom(behavior = 'smooth')Scrolls to the bottom.
scrollToLeft(behavior = 'smooth')Scrolls to the left edge.
scrollToRight(behavior = 'smooth')Scrolls to the right edge.

Styling Tokens

Every CSS custom property the scrollbar overlay paints with. Override any of them — on a single instance, a subtree, or a theme — to restyle it. See the Theming guide for the full token-layering model, or the Theme Generator to tweak these live.

TokenDescription
--gog-scroll-track-bg / -track-radiusTrack background and corner radius.
--gog-scroll-thumb-bg / -thumb-hover-bg / -thumb-active-bgDraggable thumb color, per interaction state.
--gog-scroll-thumb-radius / -thumb-insetThumb shape and inset from the track edges.
--gog-scroll-corner-bgBackground of the corner square where two tracks meet.
--gog-scroll-fade-durationAuto-hide fade animation timing.
--gog-scroll-focus-ring / -focus-ring-widthKeyboard focus ring on the viewport.
--gog-scroll-{normal|thin}-track-width / -thumb-min-sizeTrack width and minimum thumb length, per size step.