Skip to content
Black FridayBlack Friday launch price: Lifetime $149 until 30 Nov. Regular price $299 from 1 Dec.ends in 15d 03:13:41Get Lifetime →
loopkit
proNew240 lines · framer-motion

CTA Sticky Bar

A pinned prompt that never leaves the page it lives on. A CTA bar that stays pinned to the top or bottom of its own small scrollable mock page via CSS `position: sticky`, demonstrated inside a self-contained scroll area so the pin never touches the browser window.

Use it for
A persistent prompt inside a scrollable panel, feed or modal body — anywhere a CTA needs to stay reachable without hijacking page scroll.
Themes
Light + dark
License
Commercial use
Live

Stays in view

A call to action that scrolls with the page, not past it

This bar lives inside its own scroll area below and stays pinned to it as the page moves underneath — never the browser window.

1

Start from a clean plan

Lay out the week before it starts, then let the board carry the detail so nothing has to live in your head.

2

Keep every update in one thread

Comments, files and status changes land next to the task they belong to, in the order they happened.

3

See where time actually goes

A simple weekly view shows which work took longer than planned, without exporting anything.

4

Hand off without a meeting

Assign an owner and the context travels with the task, so the next person picks up exactly where you left off.

5

Review before it ships

A short checklist sits next to each task so nothing moves forward half-finished.

6

Keep a record that makes sense later

Every change stays in order, so looking back next quarter still tells a clear story.

Your plan updates automatically as the team moves.

tsx
"use client";

import * as React from "react";
import { motion, useReducedMotion } from "framer-motion";
import { cn } from "@/lib/utils";

function useReducedMotionSafe() {
  const prefersReduced = useReducedMotion();
  const [mounted, setMounted] = React.useState(false);
  React.useEffect(() => setMounted(true), []);
  return mounted ? !!prefersReduced : false;
}

/**
 * A CTA bar that pins itself to the bottom (or top) of its own scroll area —
 * never the browser window. The block builds a small scrollable mock "page"
 * (a fixed-height `overflow-y-auto` container with a few mock content
 * blocks) and puts the bar inside it as a `sticky` child, so CSS keeps it
 * pinned to that container's own scroll, not `window`. No `position: fixed`,
 * no portal.
 */

The rest is in Pro.

Full source, the rebuild prompt and registry install unlock with Pro or Lifetime.

Get Pro

How it behaves

  • →The demo scroll area is a fixed-height (520px) overflow-y-auto container; the bar is a sticky top-0 or sticky bottom-0 child of that same container, so it pins to its own scrolling ancestor only — window scroll and position: fixed are never used.
  • →position swaps which edge the bar sticks to and flips its border/shadow side to match.
  • →tone swaps the bar between a neutral bg-foreground treatment and a bg-signal treatment, both theme-token driven.
  • →A soft pulse glow behind the CTA button is gated by an IntersectionObserver + document.hidden check, so it only animates while the block is onscreen and the tab is visible, and is skipped under prefers-reduced-motion.
  • →dismissible adds a close control that hides the bar via local state (no native confirm/alert) and calls onDismiss.
  • →Mock content is six generic, scrollable content cards with no invented metrics, used purely to demonstrate the pin while scrolling.

Accessibility

  • ✓The bar's CTA is a real `<button>` with visible text, not an icon-only control; the dismiss control has an `aria-label`.
  • ✓The decorative pulse glow is `aria-hidden` and purely visual, never affecting layout or focus order.
  • ✓Scroll container keeps native keyboard/wheel scrolling (`overflow-y-auto` on a focusable, taggable element) rather than a custom scroll hijack.
  • ✓Honours prefers-reduced-motion.