Skip to content

Intro Disclosure

OnboardingMIT
View in Storybook

First-time-user disclosure card with multi-step walkthrough.

bun add @otfdashkit/ui

Preview

Intro Disclosure
Loading Storybook preview…

Code

import React, { useState } from 'react'
import { IntroDisclosure, Button } from '@otfdashkit/ui'
import { Sparkles, Users, GitBranch, Rocket } from 'lucide-react'

const INTRO_STEPS = [
  {
    title: 'Welcome to OTF',
    description: 'A typed, accessible component library + AI-ready kits. Ship faster, look premium by default.',
    media: (
      <div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-primary/20 via-primary/5 to-transparent">
        <Sparkles className="h-16 w-16 text-primary" strokeWidth={1.25} />
      </div>
    ),
  },
  {
    title: 'Bring your team',
    description: 'Invite collaborators, assign roles, and share context across projects.',
    media: (
      <div className="absolute inset-0 flex items-center justify-center bg-[hsl(var(--muted)/0.5)]">
        <Users className="h-16 w-16 text-foreground/70" strokeWidth={1.25} />
      </div>
    ),
  },
  {
    title: 'Connect your repo',
    description: 'Link a GitHub repository to sync issues and let Claude ship pull requests.',
    media: (
      <div className="absolute inset-0 flex items-center justify-center bg-[hsl(var(--muted)/0.5)]">
        <GitBranch className="h-16 w-16 text-foreground/70" strokeWidth={1.25} />
      </div>
    ),
  },
  {
    title: 'Ship something',
    description: 'You\'re ready. Open the command palette (⌘K) and start a task.',
    media: (
      <div className="absolute inset-0 flex items-center justify-center bg-gradient-to-tr from-primary/15 to-transparent">
        <Rocket className="h-16 w-16 text-primary" strokeWidth={1.25} />
      </div>
    ),
  },
]

export function Example() {
  function Demo() {
    const [open, setOpen] = useState(true)
    return (
      <div className="relative h-[600px] w-[800px] bg-[hsl(var(--background))] rounded-md border border-[hsl(var(--border))] overflow-hidden">
        <div className="absolute inset-0 flex items-center justify-center">
          <Button onClick={() => setOpen(true)}>
            <Sparkles className="h-4 w-4 mr-2" /> Show intro
          </Button>
        </div>
        <IntroDisclosure
          open={open}
          onOpenChange={setOpen}
          eyebrow="What's new"
          steps={INTRO_STEPS}
          storageKey={undefined}
        />
      </div>
    )
  }
  return <Demo />
}

Free with the SDK. This pattern ships in @otfdashkit/ui under the MIT license.

Props

Props declared in the IntroDisclosure source. Inherited HTML attributes are not listed.

PropTypeDefaultDescription
stepsrequiredIntroStep[]The steps to walk through, in order.
eyebrowstringOptional extra label e.g. "What's new in 2.0".
finishLabelstring'Get started'Final-step CTA label. Default "Get started".
onOpenChange(open: boolean) => voidCalled when the user dismisses (skip / done / backdrop / escape).
openbooleanControlled open prop. If omitted, falls back to localStorage on mount.
skipLabelstring'Skip'Skip button label. Default "Skip".
storageKeystring'otf:intro-seen'localStorage key. Once seen, the component stays closed for that key. Set to `undefined` to disable persistence (always opens).