Skip to content
goodtabs UI
BrowseBrowse components

Hero blueprint

A hero on a dotted grid with dashed guides and dimension labels around a content slot.

npx shadcn@latest add https://ui.goodtabs.co/r/hero-blueprint.json

With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/hero-blueprint

Design systems

Measure twice, ship once

Spacing, radii and type on one grid, so every screen lines up.

Eyebrow, title, one line and two actions on the grid.

With a live slot

Live preview

Every control, to the pixel

A real control group in the slot, framed by the guides.

Usage

import { HeroBlueprint } from "@/components/hero-blueprint"
import { Button } from "@/components/ui/button"

<HeroBlueprint
  eyebrow="Design systems"
  title="Measure twice, ship once"
  actions={<Button>Read the spec</Button>}
/>

Props

PropTypeDefaultDescription
eyebrowstringSmall uppercase line above the title.
titleReactNodeThe headline.
descriptionReactNodeOne or two sentences under the title.
actionsReactNodeButtons and links.
childrenReactNodeThe live slot below the actions: a demo, screenshot or control.

Source

hero-blueprint/hero-blueprint.tsx
import * as React from "react";
import { cn } from "@/lib/utils";

type HeroBlueprintProps = Omit<React.ComponentProps<"section">, "title"> & {
  /** Small uppercase line above the title. */
  eyebrow?: string;
  /** The headline. Pass a string or your own element. */
  title: React.ReactNode;
  /** One or two sentences under the title. */
  description?: React.ReactNode;
  /** Buttons and links, usually one primary and one outline. */
  actions?: React.ReactNode;
  /** The live slot: a demo, a screenshot or an interactive element, below the actions. */
  children?: React.ReactNode;
};

/** A dimension line: ticks at both ends, a hairline between and a mono label in the middle. */
function Dimension({ label }: { label: string }) {
  return (
    <div aria-hidden="true" className="flex items-center font-mono text-[10px] text-muted-foreground">
      <span className="h-2 w-px bg-border" />
      <span className="h-px flex-1 bg-border" />
      <span className="px-2 leading-none">{label}</span>
      <span className="h-px flex-1 bg-border" />
      <span className="h-2 w-px bg-border" />
    </div>
  );
}

/**
 * A hero drawn like a technical sheet: a dotted grid, dashed guides at the content edges and dimension labels.
 * Static server component. The content sits in front, so the drawing never carries information.
 */
function HeroBlueprint({ eyebrow, title, description, actions, children, className, ...props }: HeroBlueprintProps) {
  return (
    <section
      data-slot="hero-blueprint"
      className={cn(
        "relative isolate overflow-hidden bg-background bg-[radial-gradient(var(--border)_1px,transparent_1px)] bg-size-[16px_16px] py-16 sm:py-20",
        className,
      )}
      {...props}
    >
      <span
        aria-hidden="true"
        className="absolute inset-x-0 top-16 -z-10 border-t border-dashed border-border sm:top-20"
      />
      <span
        aria-hidden="true"
        className="absolute inset-x-0 bottom-16 -z-10 border-b border-dashed border-border sm:bottom-20"
      />
      <div className="relative mx-auto w-full max-w-3xl px-4">
        <div className="absolute inset-y-0 left-4 w-px border-l border-dashed border-border" aria-hidden="true" />
        <div className="absolute inset-y-0 right-4 w-px border-r border-dashed border-border" aria-hidden="true" />
        <div className="absolute inset-x-4 -top-9 sm:-top-11">
          <Dimension label="max 768" />
        </div>
        <div className="relative flex flex-col items-center gap-4 bg-background px-4 py-10 text-center sm:px-10 sm:py-14">
          {eyebrow && <p className="m-0 font-mono text-xs tracking-wide text-muted-foreground uppercase">{eyebrow}</p>}
          <h1 className="m-0 max-w-2xl text-3xl leading-none font-medium tracking-tight text-balance sm:text-5xl">
            {title}
          </h1>
          {description && <p className="m-0 max-w-xl text-sm text-muted-foreground sm:text-base">{description}</p>}
          {actions && <div className="flex flex-wrap justify-center gap-2">{actions}</div>}
          {children && <div className="mt-4 w-full">{children}</div>}
        </div>
        <span
          aria-hidden="true"
          className="absolute right-6 -bottom-9 font-mono text-[10px] text-muted-foreground sm:-bottom-11"
        >
          16 px grid
        </span>
      </div>
    </section>
  );
}

export { HeroBlueprint };

For AI agents

Hand your agent this page, the markdown at /components/hero-blueprint/prompt.md, or the Copy AI prompt button above.

When to use

  • A landing page for a design system, a token set or a tool for builders
  • When the product is about precision and a drawn grid backs that up
  • With a live slot to show the real thing inside the guides

When not to use

  • Consumer or editorial pages, where the technical drawing feels cold
  • More than once on a page, or inside app screens
  • Over a photo or a busy background, which hides the dots

Agent notes

  • Static server component with no motion; the grid, guides and labels are aria-hidden decoration
  • The content block has a background, so the dots stop at its edge and text keeps its contrast
  • The labels are fixed text (max 768, 16 px grid) that match max-w-3xl and the 16px dot spacing; edit them if you change either
  • Guides sit at the section's vertical padding, so className overrides of py-* should move top and bottom offsets too

The maths

  • Dots repeat every 16px, which is 4 × the 4px unit, so every spacing token lands on a dot.
  • The content box is max-w-3xl = 48 × 16 = 768px, and the dimension line is labelled with that number.
  • Guides sit at top-16 and bottom-16, 64px or 4 grid cells in, with the same offset on both sides so the frame is symmetric.