DirectiveOptional

wrAffix

Stick an element to the top on scroll and style it while it is stuck.

Installation

Import the class, and @use the entry point in your global styles: without it the component renders unstyled.

import { Component } from '@angular/core';
import { WrAffix } from 'ngwr/affix';

@Component({ imports: [WrAffix] })
export class MyComponent {}

Usage

Style the stuck state with .wr-affix--active.

<header wrAffix [wrAffixOffsetTop]="0" (wrAffixChange)="onAffix($event)">
  …
</header>
header.wr-affix.wr-affix--active {
  box-shadow: var(--wr-shadow-sm);
  background: var(--wr-color-surface);
}

Demo (scroll inside the box)

Sticky header — affixed: false
Scroll content row #1
Scroll content row #2
Scroll content row #3
Scroll content row #4
Scroll content row #5
Scroll content row #6
Scroll content row #7
Scroll content row #8
Scroll content row #9
Scroll content row #10
Scroll content row #11
Scroll content row #12
Scroll content row #13
Scroll content row #14
Scroll content row #15
Scroll content row #16
Scroll content row #17
Scroll content row #18
Scroll content row #19
Scroll content row #20
Scroll content row #21
Scroll content row #22
Scroll content row #23
Scroll content row #24
Scroll content row #25
Scroll content row #26
Scroll content row #27
Scroll content row #28
Scroll content row #29
Scroll content row #30
Scroll content row #31
Scroll content row #32
Scroll content row #33
Scroll content row #34
Scroll content row #35
Scroll content row #36
Scroll content row #37
Scroll content row #38
Scroll content row #39
Scroll content row #40

In a grid or flex parent

The sentinel leaves the flow there, so it adds no gap and takes no grid cell. The parent gets the wr-affix__container class, which makes it position: relative while an affix is in it, so its absolutely positioned children anchor to it. A position you give the parent yourself wins. Give the host align-self: start: a stretched item fills its row and has no room to stick.

<div style="display: grid; grid-template-columns: 12rem 1fr; gap: 1rem">
  <aside wrAffix [wrAffixOffsetTop]="16" style="align-self: start">…</aside>
  <main>…</main>
</div>

The sidebar sticks once this row has scrolled away.

Sidebar — affixed: false
Content row #1
Content row #2
Content row #3
Content row #4
Content row #5
Content row #6
Content row #7
Content row #8
Content row #9
Content row #10
Content row #11
Content row #12
Content row #13
Content row #14
Content row #15
Content row #16
Content row #17
Content row #18
Content row #19
Content row #20
Content row #21
Content row #22
Content row #23
Content row #24
Content row #25
Content row #26
Content row #27
Content row #28
Content row #29
Content row #30

API

NameDescriptionTypeDefault
[wrAffix]Makes the host sticky and adds wr-affix--active while it is pinned.directive—
[wrAffixOffsetTop]Pixels from the top of the scroll container when stuck.number0
(wrAffixChange)Emits true on stick and false on release.boolean—
.wr-affixAlways on the host. Sets z-index: 10; override it here to restack.class—
.wr-affix--activeOn the host while pinned.class—

See also