PParable
All components

Section Dots

Interactive

A scroll-spy side rail whose active dot stretches into a labelled pill, tracking the section in view with IntersectionObserver and smooth-scrolling on click.

Inspired by Skiper UI — Side Scroll Navigation (pattern) — independently built, not affiliated. Attribution policy

01
Introduction
02
Features
03
Pricing
04
FAQ

Installation

npx shadcn@latest add https://parable-three.vercel.app/r/section-dots.json

Runs the shadcn CLI — the component source is copied intocomponents/parable/section-dots.tsx. You own it from there.

Usage

The exact code rendering the preview above.

<div style={{ position: "relative", width: "100%", minHeight: 380, overflow: "hidden", borderRadius: 16, background: "radial-gradient(120% 120% at 12% 0%, #17151f 0%, #0f0f10 62%)", border: "1px solid rgba(255,255,255,0.08)" }}>
    <div style={{ position: "absolute", inset: 0, padding: "30px 100px 30px 36px", display: "flex", flexDirection: "column", gap: 14 }}>
      {[
        { id: "sd-intro", tag: "01", title: "Introduction", accent: "#8b5cf6" },
        { id: "sd-features", tag: "02", title: "Features", accent: "#ec4899" },
        { id: "sd-pricing", tag: "03", title: "Pricing", accent: "#f5a623" },
        { id: "sd-faq", tag: "04", title: "FAQ", accent: "#22d3ee" },
      ].map((s) => (
        <div key={s.id} id={s.id} style={{ borderRadius: 14, padding: "14px 16px", background: "rgba(255,255,255,0.02)", border: "1px solid rgba(255,255,255,0.07)" }}>
          <div style={{ fontSize: 11, letterSpacing: 2, fontFamily: "ui-monospace, monospace", color: s.accent }}>{s.tag}</div>
          <div style={{ marginTop: 4, fontSize: 15, fontWeight: 600, color: "rgba(255,255,255,0.92)" }}>{s.title}</div>
          <div style={{ height: 6, width: "58%", borderRadius: 999, marginTop: 10, background: "linear-gradient(90deg, " + s.accent + "66, transparent)" }} />
          <div style={{ height: 6, width: "40%", borderRadius: 999, marginTop: 6, background: "rgba(255,255,255,0.08)" }} />
        </div>
      ))}
    </div>
    <SectionDots
      position="right"
      className="absolute"
      sections={[
        { id: "sd-intro", label: "Introduction" },
        { id: "sd-features", label: "Features" },
        { id: "sd-pricing", label: "Pricing" },
        { id: "sd-faq", label: "FAQ" },
      ]}
    />
  </div>

Props

SectionDotsProps extends Omit<React.HTMLAttributes<HTMLElement>, "children">

PropTypeDefaultDescription
sections*SectionDotItem[]Ordered sections to track. One dot is rendered per entry, in order.
position"right" | "left""right"Which screen edge the rail pins to. Defaults to `"right"`.
offsetnumber0Pixels from the viewport top treated as the fold — subtracted from scroll targets and from the observer's top margin so a fixed header never hides the section a click lands on. Defaults to `0`.
accentstring"#8b5cf6"Accent for the active dot, pill ring, and focus ring. Mirrors `--pb-violet`.

npm dependencies

  • motion