jixoai-ui
the terminal design language
loading the style sheet…
Skip to content
docs · print medium: screen

registry:ui · NativeHTML

progressive blur — content scrolls UNDER a pinned head and diffuses

The same-layer-sticky practice made literal: a list scrolling inside its own container gets a pinned head, and whatever passes beneath it diffuses instead of slicing. Stacked backdrop-filter bands, each masked to its own gradient rung, ramp blur from ~clear at the inner edge to the full stack at the scrollport edge. Zero JS: the pin is a sticky h-0 root (absolutely positioned overlays scroll away — probed), and the reveal rides the CSS scroll timeline, so nothing blurs while the list rests at the top.

zero JS sticky h-0 pin scroll-timeline reveal

progressive blur

THE docs-rail composition (left): the head pins at the edge, the list scrolls UNDER it through the progressive band — scroll the card and watch the entries diffuse beneath the pinned title/filter. Right: the always-painted static band at the bottom edge, the Magic UI default posture.

sticky head · reveal scroll — the rail law

components

  • entry-01
  • entry-02
  • entry-03
  • entry-04
  • entry-05
  • entry-06
  • entry-07
  • entry-08
  • entry-09
  • entry-10
  • entry-11
  • entry-12
  • entry-13
  • entry-14
  • entry-15
  • entry-16
  • entry-17
  • entry-18

position bottom · reveal static

  • entry-01
  • entry-02
  • entry-03
  • entry-04
  • entry-05
  • entry-06
  • entry-07
  • entry-08
  • entry-09
  • entry-10
  • entry-11
  • entry-12
  • entry-13
  • entry-14
  • entry-15
  • entry-16
  • entry-17
  • entry-18
usage.svelte svelte
<script lang="ts">
  import ProgressiveBlur from '@ui/progressive-blur';
</script>

<!-- an EARLY child of the scroll container — the sticky h-0 root
     pins to the scrollport edge through the whole scroll range -->
<div class="relative h-72 overflow-auto">
  <ProgressiveBlur position="top" reveal="scroll" class="z-[5]" />
  <!-- the sticky head pins ABOVE the band (z-10); the list scrolls
       under both through the progressive fade -->
  <div class="sticky top-0 z-10">…title + filter…</div>
  <!-- …the scrolling list… -->
</div>

the technique

How the ladder works

Each layer blurs everything painted beneath it — including the earlier layers — so stacking masked runs compounds. With the default eight levels, layer i owns the rung between i·12.5% and (i+3)·12.5% of the band: near the inner edge only the half-pixel layer contributes; at the scrollport edge the whole 0.5→64px stack is active. The reveal animates opacity on EACH LAYER (an opacity on a common ancestor would create a backdrop root and the layers would sample nothing beneath themselves), driven by scroll(nearest) over --jx-pblur-ramp (72px default).

the formula
/* layer i of n — the whole ladder is this one formula */
step = 100 / n;
mask: linear-gradient(to edge,
  transparent i·step, opaque (i+1)·step,
  opaque (i+2)·step, transparent (i+3)·step);
backdrop-filter: blur(levels[i]px);

types

Types

The band hangs from any scrollport edge; the pin dialect picks the positioning law; reveal chooses resting paint or scroll-in fade.

position top / bottom

The band hangs into the viewport from that edge via a sticky h-0 root.

position both

Two pinned roots — keeps both edges progressive instead of one uniform-blur element.

position start / end / inline

The inline-axis edges of a horizontal-overflow strip; inline renders the start+end pair as two roots.

pin sticky / grid

sticky = the zero-height root inside a scroller (any edge); grid = a position-free item of the host one-cell grid — inline edges only, else it is a compile error.

hold (grid only)

The outer share (0–100) holding the ladder peak — the ramp compresses inboard for strips with a blank control lane at the clip edge.

reveal static / scroll

static paints always (Magic UI parity); scroll fades the ladder in with the nearest scroller — inline edges keep the static law.

usage

Usage

Mount the band as an EARLY child of the scroll container, before the sticky head it sits under.

ProgressiveBlur usage
<script lang="ts">
  import ProgressiveBlur from '@ui/progressive-blur';
</script>

<!-- an EARLY child of the scroll container — the sticky h-0 root
     pins to the scrollport edge through the whole scroll range -->
<div class="relative h-72 overflow-auto">
  <ProgressiveBlur position="top" reveal="scroll" class="z-[5]" />
  <!-- the sticky head pins ABOVE the band (z-10); the list scrolls
       under both through the progressive fade -->
  <div class="sticky top-0 z-10">…title + filter…</div>
  <!-- …the scrolling list… -->
</div>

a11y

Accessibility

Pure scenery: the band is aria-hidden, pointer-events-none decoration over the scroller's real content.

ARIA

AttributeValueDescription
aria-hiddentrueThe band is decoration; screen readers skip it entirely
pointer-eventsnoneThe band never intercepts pointer input over the scrolling content
scroll()@supports-gatedEngines without scroll timelines keep the resting clean state — never a wrongly-painted band

theming

Theming

No density footprint — the band is dimensionless chrome; its one token tunes the scroll-in ramp distance.

xs
pinned head
  • entry-01
  • entry-02
  • entry-03
  • entry-04
  • entry-05
  • entry-06
  • entry-07
  • entry-08
sm
pinned head
  • entry-01
  • entry-02
  • entry-03
  • entry-04
  • entry-05
  • entry-06
  • entry-07
  • entry-08
default
pinned head
  • entry-01
  • entry-02
  • entry-03
  • entry-04
  • entry-05
  • entry-06
  • entry-07
  • entry-08
lg
pinned head
  • entry-01
  • entry-02
  • entry-03
  • entry-04
  • entry-05
  • entry-06
  • entry-07
  • entry-08
TokenDefaultSource
--jx-pblur-ramp72pxcomponent
height'6rem'component

axes

Universal props

The eight-axis surface (explicit-props): size · shape · radius · density · color · theme · elevation · motion — each axis takes named steps, auto (inherit the ambient context; stamps nothing), an exact number (px · coefficient · dp · hue per axis), or query() for responsive/container-conditional values. A FLAT subtractive veil by design (the 减色墨律's own child): the band's FIRST-TIME Defaults contract carries all eight axes with NO owns — no elevation rung, nothing to stamp absent an explicit lane. The supply chain is the point: the axis tree stays continuous through a band.

ProgressiveBlur · universal props

…scrolls…

progressive-blur-universal.svelte svelte
<ProgressiveBlur pin="grid" position="top" density="small" />

api

API

Props from the ProgressiveBlurProps interface (a dialect-discriminated union); the component's MECHANISM ships no script at all — the props lane is JS (resolution + stamps), the popover zero-script precedent.

Properties

PropertyTypeDefaultDescription
pin 'sticky' | 'grid''sticky'How the band pins to its edge. sticky = the zero-layout sticky root inside a scroller (any edge); grid = a position-free item of the host one-cell grid (grid-area 1/1 + justify-self per edge, inline edges only).
position 'top' | 'bottom' | 'both' | 'start' | 'end' | 'inline''bottom'Which scrollport edge(s) the band hangs from: both = the block pair, inline = the start+end pair (the horizontal-overflow strip). Narrows to start | end under pin=grid — other combos are compile-time errors (the runtime fallback is start).
hold number0Grid dialect only: the outer share of the band (0–100, clamped) that holds the ladder peak instead of ramping — the ramp compresses into the inboard (100-hold)% for strips whose readable content parks inboard of the clip edge.
height string'6rem'Band size along its hang axis — any definite CSS length (px/rem); % unsupported.
blurLevels number[][0.5, 1, 2, 4, 8, 16, 32, 64]Per-layer blur px, inner-edge first; at least 2 levels (fewer falls back to the default ladder).
reveal 'static' | 'scroll''static'static = always painted; scroll = fades in with the nearest scroller (@supports-gated; inline edges keep the always-painted static law).
class string''Class passthrough to each pinned root.

Universal props

PropertyTypeDefaultDescription
size'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopethe base scale — root font-size; parts size in em
shape'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto''auto' · ambient scopecorner geometry (CSS corner-shape; §14 degrade table)
radius'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopecorner size; auto = the concentric broadcast (§3)
density'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopespacing/leading scale over the kernel channels (§4)
color'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string'auto' · ambient scopethe hue axis of the fixed oklch primary system (§5)
theme'light' | 'dark' | 'system' | 'auto''auto' · ambient scopelight/dark profile; system = the JS-mutable global (§6)
elevation'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number'auto' · ambient scopeofficial M3 levels over the surface ladder (§7)
motion'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number'auto' · ambient scopeintensity across the motion kernels (§8)
jixoai-ui ui.jixoai.com the jixoai design language

Theme

Brand hue

0°

Auto cycle · 24h