UxStickyBand
The page's sticky band: its backing and its switch. While mounted, the band (useStickyBand) gathers the pinned members — the global Filter (band), NavJumpPills (joins on its own) and, with `title`, the page's UxPageTitle — and publishes --q-sticky-top so table headers pin under it. The backing spans the content column at the band's height, fades in (400ms) once anything pins, and carries a two-layer shadow. c104: filter and pills side by side. c106 (`title`): two columns — title and the pills docked under it, beside the filter — compacting once the title pins (title 15px with its hairline faded out, filter values only, outlined pills). Nothing in the band changes the page's flow.
The sticky band is the page's pinned header: the global Filter, the page title (UxPageTitle) and the sub nav (NavJumpPills) pin together over one backing, and table headers pin underneath it. It is page-level — one band per page, driven by the window's scroll — so the live demo runs in a frame: scroll inside it. Its panel (bottom right) switches between a detail page (title + pills) and a listing (title only), and logs every mode change with the page's height and content position, which must read Δ0.
Set it up — once, in the layout
Mount UxStickyBand at the top of the content column, inside the relative wrapper that holds the global filter, with negative margins over the column's padding. Pass title for the two-column band. Pages need nothing: the title and the pills join on their own.
<main class="flex-1 min-w-0 px-8 py-8">
<div class="relative">
<!-- the band: its backing and its switch (title = the c106 two-column band) -->
<UxStickyBand title class="-mx-8" />
<!-- the global filter, a band member -->
<div class="pointer-events-none absolute inset-0 -top-10 z-40 flex flex-col items-end [&>*]:pointer-events-auto">
<Filter band :stickyTop="8" v-model:keyValFilters="filters" … />
</div>
<slot />
</div>
</main><!-- a direct child of the page: no wrapper around the title -->
<UxPageTitle :breadcrumb="crumbs" :rowMinHeight="44">Payments</UxPageTitle>
<p>Who is on the Payments team…</p>
<TeamHeader … /> <!-- not part of the band -->
<NavJumpPills :sections="SECTIONS" /> <!-- joins on its own -->
<section id="installs">…</section>Two layouts
<UxStickyBand />The filter and the sub nav pin side by side at the pin line (8px). The band is as tall as the taller of them; nothing restyles. The page title scrolls away.
<UxStickyBand title />Two columns. Left: the page title (row 1) and, docked under it, the sub nav (row 2). Right: the filter. The band is as tall as the taller column, so a tall filter never opens space between title and pills. Once the title pins, the band compacts.
Members
| Component | Joins when | Pinned (c106, compact) | Opt out |
|---|---|---|---|
UxPageTitle | a band runs with title | 15px, its hairline faded out; back link, #right, #far-right, #chin step aside; settles back in place on unpin. Drives the scroll mode. | :sticky="false" |
NavJumpPills | any band is mounted | Docks under the title; no fill, grey-350 outline; black semibold; active purple bold with a 2px ring. Pill changes take 200ms. | :sticky="false" |
Filter | it has band | Values only — the label rows and ADD step aside. | :bandCompact="false" keeps the labels |
UxTableListHeader | always reads the band | Pins at --q-sticky-top and eases with the band. compact tables pin at 0. | — |
Scrolling (c106)
- rest — the title hasn't pinned: nothing restyles. The backing never shows at scroll 0.
- down — the title has pinned: the band compacts (1s, quint-in-out). When the pills reach their line they dock and the band extends under them (200ms, ease-out) — until then it ends under the title, so the pills rise into open space.
- up — tracked (24px of travel flips it) but drawn exactly as down while
STICKY_BAND.upRestylesisfalse. On, it is an in-between set — title 20px, filter labels back — growing over 2s. - A pill jump holds the mode through its smooth scroll and lands 16px under the compact band.
- Members style by
look(the mode as drawn), never bymode.prefers-reduced-motiondrops every transition.
Rules for consumers
- Nothing in the band may change the page's flow. The title and the pills are sticky boxes that keep their rest height and restyle inside it; the filter and the backing take no flow space. That is why a mode change can't feed back into the scroll that caused it.
- The title must be a direct child of a tall element (the page). A sticky box can't leave its parent — a wrapper around
UxPageTitletraps it. UserowMinHeightinstead of a wrapper that sets the row's height. - One title drives the band. The first
UxPageTitleto join holds the slot; others on the page stay plain. - Scroll boxes reset the band. A vertical scroll box holding a non-compact table sets
--q-sticky-top: 0pxinside (UxModalandUxOverflowXalready do), or its header is pushed down within it.
Published on <html>
| --q-sticky-top | The whole band (px) — table headers pin here. |
| --q-sticky-top-down | The band compact with the sub nav docked — where pill jumps land. |
| --q-sticky-row1 | Where the sub nav pins: under the title's row (c106 only; c104 pins at stickyTop). |
| --q-sticky-filter-w | The filter bar's width — the pinned title stops short of it. |
| --q-band-dur / --q-band-ease | The motion of the change in progress: 1s compacting, 2s growing, 200ms docking. |
| --q-band-fade | The backing's fade (400ms), quicker than the motion. |
STICKY_BAND (auto-imported)
Geometry and motion in one place — every row has a fixed height per look, so the band's height is known before it is drawn (a jump has to land under the compact band before the band has compacted).
| pinTop | 8 | The pin line; rows are padded by the same. |
| titleRow | 32 | The pinned title's row. |
| titleSize | { down: 15, up: 20 } | The pinned title's size per look. |
| navRow | { rest: 42, down: 30, up: 30 } | The sub nav's row per look (its sticky box keeps the rest height). |
| navBottomPad | 16 | Space under the docked pills. |
| jumpGap | 16 | Between the band and a landed heading. |
| hysteresis | 24 | Travel before the scroll mode flips. |
| upRestyles | false | Whether scrolling up draws the in-between set. |
| motion | quint-in-out · 1000 / 2000 · fade 400 · dock 200 (ease-out) · quick 200 | Compact / grow durations, the backing's fade, the dock, the pills. |
useStickyBand() exposes the state for custom members and readouts: mode, look, top, topDown, titleBand, showBacking, and onFlip(fn) for instrumentation.
Usage
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | Boolean | false | Run the c106 two-column band: UxPageTitle joins and pins on row 1 beside the filter, NavJumpPills docks under it, and the band compacts while scrolling (scroll-up draws the same while STICKY_BAND.upRestyles is off). Off: the c104 band — filter and pills side by side at the pin line. |