Sticky Elements v2.0

Premium sticky positioning with visual state indicators, accessibility features, and responsive breakpoints

Quick Start

1. Include the CSS and JavaScript

Required Files HTML
<link rel="stylesheet" href="styles/sticky-elements.css">
<script src="javascript/sticky-elements.js"></script>

2. Add the data-rsl-sticky Attribute

Basic Usage HTML
<!-- Basic sticky header -->
<header data-rsl-sticky data-sticky-variant="header">
    <nav>...</nav>
</header>

<!-- Sticky sidebar with offset -->
<aside data-rsl-sticky data-sticky-offset="lg" data-sticky-variant="sidebar">
    <nav>Table of Contents</nav>
</aside>

<!-- Responsive sticky (only on desktop) -->
<div data-rsl-sticky data-sticky-breakpoint="lg">
    Only sticky on screens 1024px and wider
</div>

Backward Compatible

V2 is fully backward compatible with V1 CSS classes (.sticky-element, .sticky-top, etc.). Existing implementations continue to work unchanged.

V1 vs V2 Comparison

Feature V1 (CSS Classes) V2 (Data Attributes)
Basic Sticky .sticky-element data-rsl-sticky
Position Variants .sticky-top, .sticky-top-md data-sticky-offset="md"
Responsive .sticky-md-up, .sticky-lg-up data-sticky-breakpoint="md"
Z-Index .sticky-z-50 data-sticky-z="high"
Stuck Detection Not available .rsl-sticky-stuck (auto-applied)
Shadow on Stuck Not available data-sticky-shadow="lg"
Presets Not available data-sticky-variant="header"
Stacking Groups Not available data-sticky-group="nav"

Data Attributes API

Configure sticky elements declaratively using data attributes:

Core Attributes

Attribute Values Default Description
data-rsl-sticky - - Initialize V2 sticky behavior on element
data-sticky-offset 0, none, xs, sm, md, lg, xl 0 Distance from top when stuck
data-sticky-variant header, sidebar, tabs, toc, action, table-header - Preset configuration for common use cases
data-sticky-breakpoint xs, sm, md, lg, xl, xxl - Only sticky at this breakpoint and above
data-sticky-z low, base, high, overlay base Z-index layer

Visual Effects

Attribute Values Default Description
data-sticky-shadow none, sm, default, lg default Shadow intensity when stuck
data-sticky-bg-stuck true, false false Apply frosted glass background when stuck
data-sticky-border true, false false Show bottom border when stuck
data-sticky-scale true, false false Slight scale-down effect when stuck

Advanced Features

Attribute Values Description
data-sticky-direction up Only show when scrolling up (hide on scroll down)
data-sticky-group String Group name for stacking multiple sticky elements
data-sticky-stack Number Order within a sticky group (lower = higher)
data-sticky-skip-link true, false Add accessibility skip link (default: true for header variant)
data-sticky-label String Label for screen reader announcements
data-filterbus-publish String FilterBus key to publish stuck state changes

JavaScript API

Methods

Method Parameters Description
RSL.Sticky.init(options) Config object Initialize all [data-rsl-sticky] elements
RSL.Sticky.create(el, options) Element, config object Create sticky instance on element
RSL.Sticky.destroy(el) Element Remove sticky behavior from element
RSL.Sticky.isStuck(el) Element Returns boolean if element is currently stuck
RSL.Sticky.getState(el) Element Returns full state object
RSL.Sticky.enable(el) Element Enable sticky behavior
RSL.Sticky.disable(el) Element Disable sticky behavior
RSL.Sticky.refresh(el) Element (optional) Recalculate positions (all if no element)

Events

Event Detail Properties Description
rsl-sticky-change element, isStuck, state Fired when stuck state changes

Example Usage

JavaScript API JS
// Initialize all sticky elements
RSL.Sticky.init();

// Create sticky programmatically
const sidebar = document.querySelector('.sidebar');
RSL.Sticky.create(sidebar, {
    offset: 'lg',
    variant: 'sidebar',
    shadow: 'sm'
});

// Check if stuck
if (RSL.Sticky.isStuck(sidebar)) {
    console.log('Sidebar is stuck');
}

// Listen for stuck state changes
sidebar.addEventListener('rsl-sticky-change', function(e) {
    console.log('Stuck:', e.detail.isStuck);
});

// Temporarily disable
RSL.Sticky.disable(sidebar);

// Re-enable
RSL.Sticky.enable(sidebar);

// Cleanup
RSL.Sticky.destroy(sidebar);

CSS Classes

V1 Legacy Classes (Still Supported)

Class Description
.sticky-element Basic sticky with 1rem top offset
.sticky-top Sticky at top: 0
.sticky-top-sm Sticky with 0.5rem offset
.sticky-top-md Sticky with 1rem offset
.sticky-top-lg Sticky with 2rem offset
.sticky-md-up Only sticky at 768px and above
.sticky-lg-up Only sticky at 1024px and above
.sticky-z-10/20/50/100 Z-index values

V2 State Classes (Auto-Applied)

Class Description
.rsl-sticky-stuck Applied when element is stuck to viewport
.rsl-sticky-hidden Applied when element is hidden (scroll direction)
.rsl-sticky-show-up Applied when visible on scroll up
.rsl-sticky-boundary Applied when element hits boundary

Utility Classes

Class Description
.rsl-sticky-disabled Force disable sticky behavior
.rsl-sticky-forced Force enable sticky behavior
.rsl-sticky-container Boundary container for sticky elements
.rsl-sticky-full-width Break out of container for full-width sticky

CSS Custom Properties

Customize the sticky elements system by overriding these CSS variables:

CSS Variables CSS
:root {
    /* Offsets */
    --rsl-sticky-offset: 0;
    --rsl-sticky-offset-xs: 0;
    --rsl-sticky-offset-sm: 0.5rem;
    --rsl-sticky-offset-md: 1rem;
    --rsl-sticky-offset-lg: 1.5rem;
    --rsl-sticky-offset-xl: 2rem;

    /* Z-index layers */
    --rsl-sticky-z-base: 100;
    --rsl-sticky-z-header: 1000;
    --rsl-sticky-z-modal: 1050;

    /* Visual effects */
    --rsl-sticky-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    --rsl-sticky-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
    --rsl-sticky-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.2);

    /* Transitions */
    --rsl-sticky-transition: all 0.2s ease;

    /* Stuck state colors */
    --rsl-sticky-bg: rgba(255, 255, 255, 0.98);
    --rsl-sticky-border: rgba(0, 0, 0, 0.1);
}

Accessibility

The Sticky Elements component is designed with accessibility as a priority:

Best Practice

Use data-sticky-label to provide meaningful context for screen readers. For example: data-sticky-label="Main navigation" will announce "Main navigation is now fixed to the top of the page" when stuck.

FilterBus Integration

Sticky Elements integrates with RSL's FilterBus for both publishing stuck state changes and subscribing to filter controls for visibility filtering.

Publishing Stuck State

When a sticky element becomes stuck or unstuck, it can publish this state change to FilterBus, allowing other components to react:

Publish Mode

  • Add data-filterbus-publish="key" to broadcast stuck state changes
  • Publishes object with { element, isStuck, variant, timestamp }
  • Use case: Show "Back to Top" button when header becomes stuck
Publishing Stuck State HTML
<!-- Sticky header publishes when stuck -->
<header data-rsl-sticky data-sticky-variant="header" data-filterbus-publish="header-state">
    <nav>My App</nav>
</header>

<!-- JavaScript: Subscribe to react to stuck state -->
<script>
    RSL.FilterBus.subscribe('header-listener', function(state) {
        var headerState = state['header-state'];
        if (headerState && headerState.isStuck) {
            document.getElementById('back-to-top').classList.add('visible');
        } else {
            document.getElementById('back-to-top').classList.remove('visible');
        }
    }, { keys: ['header-state'] });
</script>

Subscribing to Filters

Sticky elements can also subscribe to FilterBus to show/hide based on filter values:

Subscribe Mode

  • Add data-filter-subscribe="key" to listen to a FilterBus channel
  • Add data-filter-value="value" to specify which filter value this element matches
  • Elements without data-filter-value always show
  • "All" filter value shows all elements (case-insensitive)
Subscribing to Filters HTML
<!-- Filter publishes to FilterBus -->
<div data-rsl-filter data-filter-key="category">
    <button data-filter-value="All">All</button>
    <button data-filter-value="Sales">Sales</button>
    <button data-filter-value="Marketing">Marketing</button>
</div>

<!-- Sticky element subscribes to FilterBus -->
<aside data-rsl-sticky
       data-sticky-variant="sidebar"
       data-filter-subscribe="category"
       data-filter-value="Sales">
    Sales Dashboard Sidebar
</aside>

FilterBus Integration Attributes

Attribute Type Description
data-filterbus-publish String FilterBus key to publish stuck state changes to
data-filter-subscribe String FilterBus key to subscribe to (e.g., "region", "status")
data-filter-value String Filter value(s) this element matches. Supports comma-separated values for multi-match (e.g., "North,South"). Elements without this attribute always show.

FilterBus Events

Event Detail Description
rsl-sticky:filterbus-update { state, meta, visible } Fired when element visibility changes due to FilterBus update

Next Steps

See Examples

View examples.html for comprehensive demos

Try the Playground

Test features in playground.html

Explore Components

Browse all RSL components