Sticky / Affix

Pin elements inside the nearest scrollable container as the user scrolls. If no scrollable parent exists, the element falls back to the viewport.

Sticky Navigation Bar

Scroll down inside this container to see the sticky bar pin itself. The component automatically uses this panel as its scroll context.

Place content before the affix element so it can scroll naturally into position, then let useAffix pin it within the nearest scrollable parent.

If you drop the same markup into a normal page without an overflow container, it will stick against the browser viewport instead.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.

Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

Custom Offset

Use data-vd-affix-offset to control when the element becomes stuck. The value is the distance in pixels from the top of the active scroll context.

<div
  class="vd-affix vd-affix-bordered"
  data-vd-affix
  data-vd-affix-offset="60">
  <p>I stick 60px below the top of my scroll container</p>
</div>
Style Variants

Combine modifier classes to fine-tune the look when the element becomes stuck.

.vd-affix-bordered — adds a bottom border when stuck
.vd-affix-no-shadow — suppresses the default drop shadow
API Reference

Wiring

import { ref } from 'vue';
import { useAffix } from "@vanduo-oss/vd3";

const root = ref<HTMLElement | null>(null);
useAffix(root);   // wires .vd-affix / [data-vd-affix] inside root; cleanup on unmount

// react to pin / unpin
root.value?.addEventListener('affix:stuck', () => { /* … */ });

CSS Classes

ClassDescription
.vd-affixBase class for affix-enabled elements. Pairs with data-vd-affix.
.vd-stickyAlias for .vd-affix. It participates in the same JS-driven affix behavior.
.is-stuckApplied automatically while the element is pinned inside its active scroll context.
.vd-affix-no-shadowModifier that removes the default box-shadow applied in the stuck state.
.vd-affix-borderedModifier that adds a subtle bottom border instead of a shadow when stuck.

Data Attributes

AttributeDescription
data-vd-affixEnables the affix behavior on the element. Detected by useAffix(root).
data-vd-affix-offsetDistance in pixels from the top of the nearest scrollable container, or the viewport when no scrollable parent exists. Defaults to 0.

Composable API

SymbolDescription
useAffix(root)Composable — wires every .vd-affix / [data-vd-affix] inside the root ref; toggles .is-stuck via a sentinel + IntersectionObserver. Call once in setup().
(automatic cleanup)The observer and generated sentinel are removed on component unmount.

Events

EventDescription
affix:stuckFired on the element when it becomes pinned. event.detail includes the offset and active root.
affix:unstuckFired on the element when it returns to its natural position.
Accessibility
  • Sticky elements do not trap focus — keyboard navigation continues naturally past them.
  • The auto-generated placeholder prevents content from jumping, avoiding disorientation for screen magnifier users.
  • Use aria-label on sticky navigation to distinguish it from the main page nav (e.g. aria-label="Sticky toolbar").
  • Stuck state changes are visual-only; ensure the element's purpose is clear without relying on position.