# Intro Loader: Wordmark

> No images. The wordmark rises a letter at a time as the count runs, then shrinks and flies into the logo's spot in the header. The target can be anything.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/intro-loader-wordmark-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/intro-loader-wordmark-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/intro-loader-wordmark-demo-light-4f74058f568c1b38.png
- Registry item: https://keyframeui.com/r/intro-loader-wordmark-demo.json
- Categories: preloader, hero, image
- Listed: 2026-10-14, source last changed 2026-10-15

## Install

```bash
npx shadcn@latest add https://keyframeui.com/r/intro-loader-wordmark-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/intro-loader.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/intro-loader#intro-loader-wordmark-demo

## Files

### `registry/demos/intro-loader-wordmark-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { IntroLoader, IntroLoaderLine, IntroLoaderTarget } from "@/components/ui/intro-loader";

const stats = [
  { value: "€420m", label: "Committed since 2019" },
  { value: "38", label: "Companies backed" },
  { value: "11 yrs", label: "Average hold" },
];

// No photography: the wordmark rises letter by letter as the count runs, then
// flies into the logo's spot in the header. The mark and the logo share their
// type, so it lands as the logo.
export function IntroLoaderWordmarkDemo() {
  const [run, setRun] = React.useState(0);

  return (
    <div className="flex h-[var(--demo-height,36rem)] w-full flex-col overflow-hidden rounded-xl border border-zinc-200 bg-[#10291f] text-[#ece6d8] dark:border-zinc-800">
      <BrowserBar url="verso.capital" run={run} onReload={() => setRun((r) => r + 1)} />
      <IntroLoader
        key={run}
        contained
        once={false}
        mark="Verso"
        markClassName="font-serif text-[clamp(5rem,27cqw,17rem)] font-normal tracking-[-0.035em]"
        counter="bottom-right"
        counterClassName="text-[clamp(1.75rem,5cqw,3rem)] font-normal tracking-[-0.03em] text-[#ece6d8]/60"
        className="@container flex min-h-0 flex-1 flex-col"
      >
        <header className="flex items-center justify-between gap-6 px-5 pt-5 sm:px-8 sm:pt-8">
          <IntroLoaderTarget>
            <span className="font-serif text-2xl leading-none tracking-[-0.035em]">Verso</span>
          </IntroLoaderTarget>
          <nav className="flex items-center gap-5 text-sm sm:gap-7">
            {["Portfolio", "Approach"].map((item) => (
              <IntroLoaderLine key={item} from="above" className="hidden @lg:block">
                <a
                  href={`#${item.toLowerCase()}`}
                  className="text-[#ece6d8]/65 transition-colors duration-150 hover:text-[#ece6d8]"
                >
                  {item}
                </a>
              </IntroLoaderLine>
            ))}
            <IntroLoaderLine from="above">
              <a
                href="#pitch"
                className="inline-flex h-9 items-center rounded-full bg-[#ece6d8] px-4 font-medium text-[#10291f] transition-[background-color,scale] duration-150 hover:bg-white active:scale-[0.97]"
              >
                Pitch us
              </a>
            </IntroLoaderLine>
          </nav>
        </header>

        <div className="mt-auto px-5 pb-6 sm:px-8 sm:pb-8">
          <h1 className="font-serif text-[clamp(2.5rem,7.5cqw,6rem)] leading-[0.95] tracking-[-0.03em]">
            <IntroLoaderLine>Patient capital for</IntroLoaderLine>
            <IntroLoaderLine>
              companies that <span className="italic">make</span>
            </IntroLoaderLine>
            <IntroLoaderLine>real things.</IntroLoaderLine>
          </h1>
          <ul className="mt-8 grid grid-cols-3 gap-4 border-t border-[#ece6d8]/15 pt-5 sm:mt-10 sm:gap-8">
            {stats.map((stat) => (
              <li key={stat.label}>
                <IntroLoaderLine className="font-serif text-[clamp(1.5rem,3.5cqw,2.25rem)] leading-none tabular-nums">
                  {stat.value}
                </IntroLoaderLine>
                <IntroLoaderLine className="mt-2 text-xs text-[#ece6d8]/55">{stat.label}</IntroLoaderLine>
              </li>
            ))}
          </ul>
        </div>
      </IntroLoader>
    </div>
  );
}

function BrowserBar({ url, run, onReload }: { url: string; run: number; onReload: () => void }) {
  return (
    <div className="flex h-10 shrink-0 items-center gap-3 border-b border-white/10 bg-black/15 px-3">
      <div aria-hidden className="flex gap-1.5">
        {[0, 1, 2].map((dot) => (
          <span key={dot} className="size-2.5 rounded-full bg-white/15" />
        ))}
      </div>
      <button
        type="button"
        onClick={onReload}
        aria-label="Reload to play it again"
        title="Play again"
        className="grid size-7 place-items-center rounded-md text-[#ece6d8]/60 transition-[background-color,color,scale] duration-150 hover:bg-white/10 hover:text-[#ece6d8] active:scale-[0.94]"
      >
        <svg
          viewBox="0 0 16 16"
          fill="none"
          stroke="currentColor"
          strokeWidth="1.5"
          strokeLinecap="round"
          strokeLinejoin="round"
          className="size-3.5 transition-[rotate] duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]"
          style={{ rotate: `${run * 360}deg` }}
        >
          <path d="M13.5 8a5.5 5.5 0 1 1-1.6-3.9" />
          <path d="M13.5 2.5v3h-3" />
        </svg>
      </button>
      <span className="mx-auto truncate rounded-md bg-white/10 px-3 py-1 font-mono text-[11px] text-[#ece6d8]/60">
        {url}
      </span>
      <span aria-hidden className="w-[4.75rem]" />
    </div>
  );
}
```
