Hero layouts for setting the tone and getting to the point. Pick one, then make the words and visuals yours.
Simple
A headline, short supporting line, primary action, and ghost action. Left-aligned on mobile, centered on larger screens.
"use client";import { Button } from "@/components/ui/button";import { cn } from "@/lib/utils";type Hero01Props = { className?: string;};export function Hero01({ className }: Hero01Props) { return ( <section className={cn("@container w-full bg-background", className)}> <div className="mx-auto flex h-full w-full max-w-2xl flex-col items-start justify-center px-6 py-16 text-left @[32rem]:items-center @[32rem]:text-center"> <h1 className="text-balance text-2xl font-medium tracking-wide text-foreground @[32rem]:text-3xl"> Ship interfaces that feel finished </h1> <p className="mt-3 max-w-md text-pretty text-sm tracking-wide text-muted-foreground"> Ready made blocks you can restyle with your tokens. </p> <div className="mt-6 flex items-center justify-start gap-2 @[32rem]:justify-center"> <Button type="button" size="sm"> Get started </Button> <Button type="button" size="sm" variant="ghost"> Browse blocks </Button> </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add hero-01Usage
import { Hero01 } from "@/components/blocks/hero/hero-01"<Hero01 />Horizon glow
A headline and single action over a dark half-circle horizon, with soft glow and subtle noise.
"use client";import { useId } from "react";import { Button } from "@/components/ui/button";import { cn } from "@/lib/utils";type Hero02Props = { className?: string;};export function Hero02({ className }: Hero02Props) { const noiseId = useId().replace(/:/g, ""); return ( <section className={cn( "@container relative w-full overflow-hidden bg-black text-white", className, )} > <div aria-hidden="true" className="absolute inset-0" style={{ backgroundImage: [ "radial-gradient(ellipse 125% 48% at 50% 78%, rgb(255 255 255 / 0.55) 0%, rgb(210 170 255 / 0.42) 8%, rgb(150 70 210 / 0.32) 22%, rgb(90 30 140 / 0.16) 40%, transparent 62%)", "radial-gradient(ellipse 90% 35% at 50% 100%, rgb(40 10 60 / 0.9) 0%, transparent 55%)", ].join(", "), }} /> <div aria-hidden="true" className="absolute left-1/2 bottom-[-48%] h-[95%] w-[165%] -translate-x-1/2 rounded-[100%] bg-black" style={{ boxShadow: "0 -1px 0 0 rgb(255 255 255 / 0.75), 0 -8px 28px 2px rgb(255 255 255 / 0.2), 0 -28px 90px 24px rgb(170 90 230 / 0.28)", }} /> <svg aria-hidden="true" className="pointer-events-none absolute inset-0 size-full opacity-[0.7] mix-blend-soft-light" > <filter id={noiseId}> <feTurbulence type="fractalNoise" baseFrequency="0.95" numOctaves="4" stitchTiles="stitch" /> </filter> <rect width="100%" height="100%" filter={`url(#${noiseId})`} /> </svg> <div className="relative z-10 mx-auto flex h-full min-h-[28rem] w-full max-w-2xl -translate-y-14 @[32rem]:-translate-y-20 flex-col items-center justify-center px-6 py-16 text-center"> <h1 className="text-balance text-xl font-medium tracking-wide text-white @[32rem]:text-2xl"> Build once, reuse everywhere </h1> <div className="mt-6 flex items-center justify-center"> <Button type="button" size="sm" className="bg-white text-black hover:opacity-85" > Get started </Button> </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add hero-02Usage
import { Hero02 } from "@/components/blocks/hero/hero-02"<Hero02 />Inset image
A full-bleed image with just enough space around the edges to let the page breathe.
"use client";import { Button } from "@/components/ui/button";import { cn } from "@/lib/utils";type Hero03Props = { className?: string;};export function Hero03({ className }: Hero03Props) { return ( <section className={cn("@container w-full bg-background p-3 sm:p-4", className)} > <div className="relative min-h-[28rem] w-full flex-1 overflow-hidden rounded-md"> <img src="/assets/brand/demos/card-cover.png" alt="" className="absolute inset-0 size-full object-cover" /> <div aria-hidden="true" className="absolute inset-0 bg-black/45" /> <div className="absolute inset-0 z-10 flex flex-col items-start justify-end p-6 text-left sm:p-8"> <h1 className="max-w-xl text-balance text-2xl font-medium tracking-wide text-white @[32rem]:text-3xl"> Ship interfaces that feel finished </h1> <p className="mt-3 max-w-md text-pretty text-sm tracking-wide text-white/75"> Ready made blocks you can restyle with your tokens. </p> <Button type="button" size="sm" className="mt-6"> Get started </Button> </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add hero-03Usage
import { Hero03 } from "@/components/blocks/hero/hero-03"<Hero03 />Split
Copy on the left and a cover image on the right. The layout stacks on narrow widths.
"use client";import { Button } from "@/components/ui/button";import { cn } from "@/lib/utils";type Hero04Props = { className?: string;};export function Hero04({ className }: Hero04Props) { return ( <section className={cn("w-full bg-background", className)}> <div data-slot="block-split" className="flex h-full min-h-[28rem] w-full flex-col gap-4 p-4 lg:flex-row lg:items-stretch lg:gap-6 lg:p-6" > <div className="flex flex-1 flex-col items-start justify-center py-6 text-left lg:py-10"> <h1 className="max-w-md text-balance text-2xl font-medium tracking-wide text-foreground lg:text-3xl"> Ship interfaces that feel finished </h1> <p className="mt-3 max-w-sm text-pretty text-sm tracking-wide text-muted-foreground"> Ready made blocks you can restyle with your tokens. </p> <Button type="button" size="sm" className="mt-6"> Get started </Button> </div> <div className="relative min-h-[16rem] w-full flex-1 overflow-hidden rounded-md lg:min-h-0"> <img src="/assets/brand/demos/card-cover.png" alt="" className="absolute inset-0 size-full object-cover" /> </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add hero-04Usage
import { Hero04 } from "@/components/blocks/hero/hero-04"<Hero04 />Logo cloud
Centered copy surrounded by floating logos. On narrow widths, the logos become two marquees.
Plays nice with your whole stack
Drop in blocks that already speak the tools your team uses every day.
"use client";import { useEffect, useRef, type CSSProperties } from "react";import { Button } from "@/components/ui/button";import { cn } from "@/lib/utils";type Hero05Props = { className?: string;};const LEFT_LOGOS = [ { src: "/assets/icons/logos/slack.svg", x: "6%", y: "14%", s: "3.4rem" }, { src: "/assets/icons/logos/notion.svg", x: "15%", y: "52%", s: "2.9rem" }, { src: "/assets/icons/logos/linear.svg", x: "4%", y: "74%", s: "3.1rem" }, { src: "/assets/icons/logos/github.svg", x: "24%", y: "22%", s: "2.7rem" }, { src: "/assets/icons/logos/figma.svg", x: "21%", y: "78%", s: "2.9rem" }, { src: "/assets/icons/logos/stripe.svg", x: "11%", y: "33%", s: "2.6rem" }, { src: "/assets/icons/logos/intercom.svg", x: "26%", y: "58%", s: "2.5rem" },] as const;const RIGHT_LOGOS = [ { src: "/assets/icons/logos/hubspot.svg", x: "88%", y: "16%", s: "3.3rem" }, { src: "/assets/icons/logos/jira.svg", x: "79%", y: "48%", s: "2.8rem" }, { src: "/assets/icons/logos/zendesk.svg", x: "91%", y: "70%", s: "3rem" }, { src: "/assets/icons/logos/asana.svg", x: "72%", y: "20%", s: "2.6rem" }, { src: "/assets/icons/logos/dropbox.svg", x: "72%", y: "76%", s: "2.9rem" }, { src: "/assets/icons/logos/snowflake.svg", x: "84%", y: "34%", s: "2.5rem" }, { src: "/assets/icons/logos/airtable.svg", x: "74%", y: "58%", s: "2.5rem" },] as const;const MARQUEE_A = [ "slack", "notion", "linear", "github", "figma", "stripe", "intercom", "airtable", "hubspot", "jira", "zendesk", "asana", "dropbox", "snowflake",] as const;const MARQUEE_B = [ "hubspot", "jira", "zendesk", "asana", "dropbox", "snowflake", "slack", "notion", "linear", "github", "figma", "stripe", "intercom", "airtable",] as const;function LogoTile({ src, className, style,}: { src: string; className?: string; style?: CSSProperties;}) { return ( <span data-slot="hero-logo-tile" className={cn( "flex items-center justify-center rounded-xl bg-card shadow-sm", className, )} style={style} > <img src={src} alt="" className="size-[52%] object-contain brightness-0 dark:invert" /> </span> );}export function Hero05({ className }: Hero05Props) { const stageRef = useRef<HTMLDivElement>(null); useEffect(() => { const stage = stageRef.current; if (!stage) return; if (!window.matchMedia("(hover: hover) and (pointer: fine)").matches) { return; } const tiles = Array.from( stage.querySelectorAll<HTMLElement>("[data-slot=hero-logo-tile]"), ).filter((tile) => tile.offsetParent !== null); const depths = tiles.map((_, i) => 0.008 + (i % 5) * 0.005); let targetX = 0; let targetY = 0; let currentX = 0; let currentY = 0; let raf: number | null = null; const tick = () => { currentX += (targetX - currentX) * 0.08; currentY += (targetY - currentY) * 0.08; tiles.forEach((tile, i) => { const d = depths[i]; const dir = i % 2 === 0 ? 1 : -1; tile.style.transform = `translate(${currentX * d * dir}px, ${currentY * d * dir}px)`; }); if ( Math.abs(targetX - currentX) > 0.1 || Math.abs(targetY - currentY) > 0.1 ) { raf = requestAnimationFrame(tick); } else { raf = null; } }; const schedule = () => { if (!raf) raf = requestAnimationFrame(tick); }; const onMove = (event: MouseEvent) => { const rect = stage.getBoundingClientRect(); targetX = event.clientX - (rect.left + rect.width / 2); targetY = event.clientY - (rect.top + rect.height / 2); schedule(); }; const onLeave = () => { targetX = 0; targetY = 0; schedule(); }; stage.addEventListener("mousemove", onMove); stage.addEventListener("mouseleave", onLeave); return () => { stage.removeEventListener("mousemove", onMove); stage.removeEventListener("mouseleave", onLeave); if (raf) cancelAnimationFrame(raf); }; }, []); return ( <section className={cn( "@container flex w-full items-center justify-center overflow-hidden bg-background", className, )} > <style>{` @keyframes hero-05-scroll { from { transform: translateX(0); } to { transform: translateX(-33.333%); } } `}</style> <div ref={stageRef} className="relative flex h-full min-h-[28rem] w-full flex-col items-center justify-center gap-10 px-0 py-16 @[40rem]:min-h-[34rem] @[40rem]:gap-0 @[40rem]:px-6" > <div aria-hidden="true" className="pointer-events-none absolute inset-0 hidden @[40rem]:block" > {LEFT_LOGOS.map((logo) => ( <LogoTile key={`${logo.src}-${logo.x}`} src={logo.src} className="absolute will-change-transform" style={{ top: logo.y, left: logo.x, width: logo.s, height: logo.s, }} /> ))} {RIGHT_LOGOS.map((logo) => ( <LogoTile key={`${logo.src}-${logo.x}`} src={logo.src} className="absolute will-change-transform" style={{ top: logo.y, left: logo.x, width: logo.s, height: logo.s, }} /> ))} </div> <div className="relative z-10 flex max-w-md flex-col items-center px-6 text-center"> <h1 className="text-balance text-2xl font-medium tracking-wide text-foreground @[40rem]:text-3xl"> Plays nice with your whole stack </h1> <p className="mt-3 max-w-sm text-pretty text-sm tracking-wide text-muted-foreground"> Drop in blocks that already speak the tools your team uses every day. </p> <Button type="button" size="sm" className="mt-6"> Get started </Button> </div> <div aria-hidden="true" className="flex w-full flex-col gap-3 overflow-hidden @[40rem]:hidden" > <div className="flex w-max" style={{ animation: "hero-05-scroll 32s linear infinite" }}> {[...MARQUEE_A, ...MARQUEE_A, ...MARQUEE_A].map((name, i) => ( <LogoTile key={`a-${name}-${i}`} src={`/assets/icons/logos/${name}.svg`} className="mr-3 size-11 shrink-0" /> ))} </div> <div className="flex w-max" style={{ animation: "hero-05-scroll 32s linear infinite reverse", }} > {[...MARQUEE_B, ...MARQUEE_B, ...MARQUEE_B].map((name, i) => ( <LogoTile key={`b-${name}-${i}`} src={`/assets/icons/logos/${name}.svg`} className="mr-3 size-11 shrink-0" /> ))} </div> </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add hero-05Usage
import { Hero05 } from "@/components/blocks/hero/hero-05"<Hero05 />Social proof
A centered statement, short supporting line, one action, and a little social proof through avatars and a rating.
Ship interfaces that feel finished
Blocks you can drop in and restyle with your tokens.
from 200+ reviews
"use client";import { Button } from "@/components/ui/button";import { Avatar, AvatarFallback, AvatarGroup, AvatarImage,} from "@/components/ui/avatar";import { cn } from "@/lib/utils";type Hero06Props = { className?: string;};const AVATARS = [ { src: "/assets/brand/demos/avatars/avatar-1.png", alt: "User 1" }, { src: "/assets/brand/demos/avatars/avatar-2.png", alt: "User 2" }, { src: "/assets/brand/demos/avatars/avatar-3.png", alt: "User 3" }, { src: "/assets/brand/demos/avatars/avatar-4.png", alt: "User 4" }, { src: "/assets/brand/demos/avatars/avatar-5.png", alt: "User 5" },] as const;function StarIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" className={className} > <path d="M12 2.5l2.9 6.1 6.6.9-4.8 4.6 1.2 6.5L12 17.8 6.1 20.6l1.2-6.5L2.5 9.5l6.6-.9L12 2.5z" /> </svg> );}export function Hero06({ className }: Hero06Props) { return ( <section className={cn("@container w-full bg-background", className)}> <div className="mx-auto flex h-full min-h-[28rem] w-full max-w-2xl flex-col items-center justify-center px-6 py-16 text-center"> <h1 className="text-balance text-2xl font-medium tracking-wide text-foreground @[32rem]:text-3xl"> Ship interfaces that feel finished </h1> <p className="mt-3 max-w-sm text-pretty text-sm tracking-wide text-muted-foreground"> Blocks you can drop in and restyle with your tokens. </p> <Button type="button" size="sm" className="mt-6"> Get started </Button> <div className="mt-10 flex flex-col items-center gap-3 @[24rem]:flex-row @[24rem]:justify-center"> <AvatarGroup overlap="sm"> {AVATARS.map((avatar) => ( <Avatar key={avatar.src} size="sm"> <AvatarImage src={avatar.src} alt={avatar.alt} /> <AvatarFallback>?</AvatarFallback> </Avatar> ))} </AvatarGroup> <div className="flex flex-col items-center gap-1 @[24rem]:items-start"> <div className="flex items-center gap-1.5"> <div className="flex items-center gap-0.5 text-amber-400" aria-hidden="true"> {Array.from({ length: 5 }).map((_, i) => ( <StarIcon key={i} className="size-3.5" /> ))} </div> <span className="text-xs tracking-wide text-foreground">4.9</span> </div> <p className="text-xs tracking-wide text-muted-foreground"> from 200+ reviews </p> </div> </div> </div> </section> );}Installation
npx @arctis-sh/@arctis-sh/ui@latest add hero-06Usage
import { Hero06 } from "@/components/blocks/hero/hero-06"<Hero06 />