Skip to content
Neat LabsJoin the waitlist

Split hero

Headline, supporting copy, and two actions beside a numbered checklist panel. Stacks on phones.

id hero-split · revision 7a37deb08c96 · catalogue 0.1.0

Preview

Every token this component reads is set by the style, and every font is loaded or a system font.

Desktop, 1280px, lightOpen alone
Phone, 390px, lightOpen alone
Desktop, 1280px, darkOpen alone
Phone, 390px, darkOpen alone

Hover and keyboard focus

The component's own hover and focus styles, switched on for every link and button at once so you can see them without a mouse. In the previews above they work as usual.

Hover, desktop, lightOpen alone
Keyboard focus, desktop, lightOpen alone

Copy it

These two files are exactly what the previews above render. The MCP tool get_component returns the same files.

  1. Save HeroSplit.tsx with your components and styles/neat-paper.css with your styles.
  2. Import styles/neat-paper.css once, after @import "tailwindcss" in your global stylesheet.
  3. Make sure Tailwind scans the folder that holds the component.
  4. For dark mode, set data-theme="dark" on the <html> element.
  5. Add <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,[email protected],600&display=swap"> to the document head.
components/HeroSplit.tsx
export interface HeroSplitProps {
  eyebrow?: string;
  title?: string;
  body?: string;
  primary?: { label: string; href: string };
  secondary?: { label: string; href: string };
  checklist?: string[];
}

const focusRing =
  "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--nl-focus)]";

/**
 * Two-column hero: the promise and actions on the left, a checklist panel on
 * the right that shows what the product does. Stacks on phones.
 */
export function HeroSplit({
  eyebrow = "Release notes, handled",
  title = "Ship the update. We'll write it up.",
  body = "Example Studio turns merged pull requests into release notes your customers will read. Review the draft, edit anything, and publish.",
  primary = { label: "Start a trial", href: "#start" },
  secondary = { label: "See a sample", href: "#sample" },
  checklist = ["Drafts from merged pull requests", "Edits stay in your voice", "Publishes to your changelog"],
}: HeroSplitProps) {
  return (
    <section className="bg-[color:var(--nl-canvas)] px-6 py-[var(--nl-section-y)] font-[family-name:var(--nl-font-sans)] text-[color:var(--nl-ink)]">
      <div className="mx-auto grid max-w-6xl items-center gap-12 lg:grid-cols-2">
        <div>
          <p className="font-[family-name:var(--nl-font-mono)] text-xs uppercase tracking-wider text-[color:var(--nl-muted)]">{eyebrow}</p>
          <h1 className="mt-4 text-balance font-[family-name:var(--nl-font-display)] text-4xl leading-tight font-semibold sm:text-5xl">
            {title}
          </h1>
          <p className="mt-6 max-w-prose text-lg leading-relaxed text-[color:var(--nl-muted)]">{body}</p>
          <div className="mt-8 flex flex-wrap gap-3">
            <a
              href={primary.href}
              className={`rounded-[var(--nl-radius)] bg-[color:var(--nl-accent)] px-5 py-3 font-medium text-[color:var(--nl-accent-ink)] hover:opacity-90 ${focusRing}`}
            >
              {primary.label}
            </a>
            <a
              href={secondary.href}
              className={`rounded-[var(--nl-radius)] border border-[color:var(--nl-line)] px-5 py-3 font-medium hover:bg-[color:var(--nl-surface)] ${focusRing}`}
            >
              {secondary.label}
            </a>
          </div>
        </div>
        <div className="rounded-[var(--nl-radius)] border border-[color:var(--nl-line)] bg-[color:var(--nl-surface)] p-6 sm:p-8">
          <p className="text-sm font-medium text-[color:var(--nl-muted)]">What happens after you merge</p>
          <ol className="mt-6 space-y-4">
            {checklist.map((item, index) => (
              <li key={item} className="flex items-start gap-4">
                <span
                  aria-hidden="true"
                  className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[color:var(--nl-accent)] font-[family-name:var(--nl-font-mono)] text-xs text-[color:var(--nl-accent-ink)]"
                >
                  {index + 1}
                </span>
                <span className="pt-0.5">{item}</span>
              </li>
            ))}
          </ol>
        </div>
      </div>
    </section>
  );
}
styles/neat-paper.css
/* Neat Labs theme "paper" (revision 9e029aa69c45) for hero-split (revision 7a37deb08c96), catalogue 0.1.0. */
:root {
  --nl-canvas: #F5F1E8;
  --nl-surface: #FFFDF8;
  --nl-ink: #1F1B16;
  --nl-muted: #5E554A;
  --nl-line: #D9D0C1;
  --nl-accent: #B4462C;
  --nl-accent-ink: #FFFFFF;
  --nl-focus: #1F5FBF;
  --nl-font-display: "Fraunces", ui-serif, Georgia, serif;
  --nl-font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --nl-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --nl-radius: 6px;
  --nl-section-y: 96px;
}

[data-theme="dark"] {
  --nl-canvas: #16130F;
  --nl-surface: #211D18;
  --nl-ink: #F2ECE1;
  --nl-muted: #B3A999;
  --nl-line: #3A342C;
  --nl-accent: #E07A5F;
  --nl-accent-ink: #1A0F0B;
  --nl-focus: #8AB4F8;
}
MCP tool call
{
  "name": "get_component",
  "arguments": {
    "id": "hero-split",
    "style": "paper"
  }
}

In every style