Koala logo Design

Sticky headers

Use the koala-sticky-header tag helper to create a header bar that appears when the user scrolls past the original page header. It slides in from the top with a smooth transition and stays fixed until the user scrolls back up. Used on detail pages (view quote, view partner, view transaction, etc.) to keep key information and actions accessible.

How it works

The tag helper renders a zero-height sentinel element and a fixed header bar. An IntersectionObserver watches the sentinel — when it scrolls out of the viewport, the sticky header slides in. The parent element must have x-data with an isSticky property and an init() method that sets up the observer.

Attributes

Attribute Type Default Description
koala-sticky-header marker - Activates the sticky header tag helper on a <div>
koala-sticky-header-max-width string max-w-screen-2xl Tailwind max-width class for the inner container

Required Alpine.js setup

The parent element must include x-data with isSticky: false and an init() that creates an IntersectionObserver. The rootMargin: '-64px 0px 0px 0px' accounts for the fixed navbar height. In the Portal, the style also includes sidebarCollapsed awareness for the sidebar margin.

<div x-data="{
    isSticky: false,
    init() {
        const observer = new IntersectionObserver(
            ([entry]) => { this.isSticky = !entry.isIntersecting },
            { rootMargin: '-64px 0px 0px 0px' }
        );
        observer.observe(this.$refs.sentinel);
    }
}">
    <div koala-sticky-header>
        <!-- Sticky header content (title, badges, action buttons) -->
    </div>

    <!-- Page content below -->
</div>

Rendered output

The tag helper replaces the <div koala-sticky-header> with a zero-height sentinel and a fixed bar. The bar uses x-show="isSticky" with enter/leave transitions.

<div x-ref="sentinel" class="h-0"></div>
<div x-show="isSticky"
     x-transition:enter="transition ease-out duration-200"
     x-transition:enter-start="-translate-y-full opacity-0"
     x-transition:enter-end="translate-y-0 opacity-100"
     x-transition:leave="transition ease-in duration-150"
     x-transition:leave-start="translate-y-0 opacity-100"
     x-transition:leave-end="-translate-y-full opacity-0"
     x-cloak
     class="fixed left-0 right-0 z-20 bg-white dark:bg-gray-800
            border-b border-gray-200 dark:border-gray-700"
     style="top: calc(4rem + var(--env-banner-h, 0px));"
     x-bind:class="sidebarCollapsed ? 'lg:ml-16' : 'lg:ml-64'">
    <div class="max-w-screen-2xl mx-auto px-4 sm:px-6 py-3
                flex items-center justify-between gap-3">
        <!-- Child content -->
    </div>
</div>

Live demo

Scroll down within the bordered box below. When the page header scrolls out of view, a sticky header appears at the top of the container.

KQ-20260331-001 v1

New

Quote KQ-20260331-001

Created 31 March 2026

Property details

Address
42 Koala Lane, London, SW1A 1AA
Price
£450,000
Type
Sale - Freehold
Property
House

Fee breakdown

Legal fee
£850.00
Search pack
£320.00
Land Registry
£100.00
Total
£1,270.00

Client details

Name
Jane Smith
Email
jane.smith@example.com
Phone
07700 900123

Usage in a detail page

A typical detail page places the sticky header immediately after the breadcrumbs. The child content usually includes a title, status badge, and action buttons.

<div class="mb-6" x-data="{
    isSticky: false,
    init() {
        const observer = new IntersectionObserver(
            ([entry]) => { this.isSticky = !entry.isIntersecting },
            { rootMargin: '-64px 0px 0px 0px' }
        );
        observer.observe(this.$refs.sentinel);
    }
}">
    <div koala-sticky-header>
        <div class="flex items-center gap-3 min-w-0">
            <h2 class="text-lg font-semibold font-mono text-gray-900
                       dark:text-white truncate">
                @Model.Quote.Reference v@(Model.Quote.Version)
            </h2>
            <span koala-badge="Neutral" koala-badge-size="Small">New</span>
        </div>
        <div class="hidden sm:flex items-center gap-2">
            <button type="button" koala-btn="Primary">Accept</button>
        </div>
    </div>

    <!-- Original page header -->
    <h1 class="text-2xl font-bold ...">Quote @Model.Quote.Reference</h1>

    <!-- Page content -->
</div>

Custom max-width

Override the inner container's max-width with koala-sticky-header-max-width. The default is max-w-screen-2xl.

<div koala-sticky-header koala-sticky-header-max-width="max-w-4xl">
    <!-- Content constrained to max-w-4xl -->
</div>