Ruixen Pro early-bird — $59 lifetime, then $7905d05h08m19sLock in $59

Command Palette

Search for a command to run...

Docs
Service Ledger

Service Ledger

A scroll-spy services section. A sticky tab strip at the top tracks the active entry while the reader scrolls through long-form panels with deliverables, optional preview media, and inline CTAs.

Installation

Usage

import { ServiceLedger } from "@/components/ruixen/service-ledger";
 
export default function Page() {
  return (
    <ServiceLedger
      title="How we work with you"
      description="A few ways our studio plugs into your roadmap — from a focused sprint to a long-term partnership."
      entries={[
        {
          code: "01",
          meta: "4–6 weeks",
          title: "Brand foundation sprint",
          description:
            "Discovery, positioning, and a tight brand system that anchors every surface.",
          items: [
            "Stakeholder and audience interviews",
            "Positioning, voice, and a messaging matrix",
            "Logo system, type stack, and color tokens",
          ],
          image: "/services/brand-foundation.png",
          cta: { href: "#", label: "See the playbook" },
        },
        {
          code: "02",
          meta: "Ongoing",
          title: "Growth and optimization",
          description:
            "Embedded design and engineering capacity for the months after launch.",
          items: [
            "Quarterly refreshes and net-new pages",
            "Experiment briefs and A/B test scaffolding",
            "Onboarding, pricing, and upgrade flows",
          ],
        },
      ]}
    />
  );
}

Props

PropTypeDefaultDescription
titlestringprovidedSection headline rendered as an <h2>.
descriptionstringprovidedMuted lead paragraph below the headline.
entriesServiceEntry[]4 defaultsService rows rendered as a sticky-anchored vertical list.
classNamestring-Extra classes appended to the outer <section>.

Types

interface ServiceEntry {
  code: string;
  meta?: string;
  title: string;
  description: string;
  items?: string[];
  /** Preview image (rendered if `video` is not provided) */
  image?: string;
  /** Preview video — autoplays muted+loop+playsInline. Takes priority over `image`. */
  video?: string;
  /** Poster image shown before the video plays */
  poster?: string;
  cta?: {
    href: string;
    label: string;
  };
}

Features

  • Sticky tab strip with scroll-spy — service codes + durations sit at the top of the section and pin to the viewport (sticky top-0) as the reader scrolls. A rAF-throttled scroll handler picks the last entry whose top has crossed a reference line (120px from the viewport top) and highlights its tab.
  • Click to scroll — each tab is an anchor link with an intercepted click handler that calls scrollIntoView({ behavior: "smooth", block: "start" }). Hash deep-links work via the panel ids.
  • Aligned tab underline — the active tab uses border-b-2 -mb-px so its underline overlaps the strip's border-b, replacing it pixel-for-pixel and producing a clean tabbed-into-divider look.
  • Mobile horizontal scroll — tabs are flex items inside an overflow-x-auto track; meta text hides below sm so codes stay readable in narrow viewports.
  • Scroll-margin clearance — every panel has scroll-mt-24 so anchor navigation lands the content below the sticky strip, not under it.
  • Composable rows — each ServiceEntry opts into a deliverables bullet list, a preview image OR video, and a tail CTA independently. Pass only the fields you need.
  • Inline video showcase — pass video: "https://…/clip.mp4" and the panel renders a <video autoplay loop muted playsInline> with aspect-video framing, bg-muted placeholder, and ring-1 ring-border/60 for a tile look. poster is honoured for fast first paint.
  • No shadcn primitives — the component depends only on lucide-react. The tab affordance is pure JSX + Tailwind, so it installs cleanly into either Radix or Base UI registry variants.
  • Theme adaptive — every surface uses shadcn tokens (bg-background, text-foreground, text-muted-foreground, border-border), with a bg-background/90 backdrop-blur-sm frosted-glass effect on the sticky strip.