flx
blockhero-11source: ok · hit
Hero 11
A split-copy hero with a light product studio mock — issue feed, properties, and a floating agent panel that fades into the page.
npx shadcn add "https://ui.flexnative.com/r/hero-11.json"free
Where it goes
hero
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · mediumanimationmotion
iconslucide-react
Registry dependencies
buttonavatarseparatorbadge
Files3 · source cached 10/8/2026, 2:56:37 PM
registry/blocks/hero/hero-11/hero-11.tsx
'use client'
import * as React from 'react'
import { ArrowRight } from 'lucide-react'
import { motion, useReducedMotion, type Variants } from 'motion/react'
import Balancer from 'react-wrap-balancer'
import { cn } from '@/lib/utils'
import { StudioDemo } from './studio-demo'
export interface Hero11Props {
title: string
description: string
featureText?: string
featureHref?: string
animation?: 'none' | 'subtle'
variant?: 'standard' | 'compact'
}
const variantStyles = {
standard: {
section: 'pt-20 pb-0 sm:pt-28',
title: 'text-3xl sm:text-4xl md:text-5xl lg:text-[3.25rem]',
description: 'text-sm sm:text-base leading-relaxed',
header: 'gap-4 sm:gap-5',
content: 'gap-8 sm:gap-10',
studio: 'max-w-7xl',
},
compact: {
section: 'pt-14 pb-0 sm:pt-20',
title: 'text-2xl sm:text-3xl md:text-4xl',
description: 'text-sm leading-relaxed',
header: 'gap-3 sm:gap-4',
content: 'gap-6 sm:gap-8',
studio: 'max-w-6xl',
},
} as const
const easeOut = [0.22, 1, 0.36, 1] as const
const container: Variants = {
hidden: {},
visible: {},
}
const titleItem: Variants = {
hidden: { opacity: 0, y: 10, filter: 'blur(4px)' },
visible: {
opacity: 1,
y: 0,
filter: 'blur(0px)',
transition: { duration: 0.6, ease: easeOut },
},
}
const descriptionItem: Variants = {
hidden: { opacity: 0, y: 8, filter: 'blur(4px)' },
visible: {
opacity: 1,
y: 0,
filter: 'blur(0px)',
transition: { duration: 0.55, delay: 0.2, ease: easeOut },
},
}
const studioItem: Variants = {
hidden: { opacity: 0, y: 16, filter: 'blur(6px)' },
visible: {
opacity: 1,
y: 0,
filter: 'blur(0px)',
transition: { duration: 0.75, delay: 0.55, ease: easeOut },
},
}
const featureItem: Variants = {
hidden: { opacity: 0, y: 6, filter: 'blur(3px)' },
visible: {
opacity: 1,
y: 0,
filter: 'blur(0px)',
transition: { duration: 0.5, delay: 1.05, ease: easeOut },
},
}
function Reveal({
active,
variants,
className,
children,
}: Readonly<{
active: boolean
variants: Variants
className?: string
children: React.ReactNode
}>) {
if (!active) return <div className={className}>{children}</div>
return (
<motion.div variants={variants} className={className}>
{children}
</motion.div>
)
}
export function Hero11({
title,
description,
featureText,
featureHref = '#',
animation = 'none',
variant = 'standard',
}: Readonly<Hero11Props>) {
const reduce = useReducedMotion()
const animate = animation === 'subtle' && !reduce
const vs = variantStyles[variant]
const titleElement = title && (
<h1
className={cn(
'text-foreground max-w-xl font-serif font-normal tracking-tight text-balance',
vs.title,
)}
>
<Balancer>{title}</Balancer>
</h1>
)
const descriptionElement = description && (
<p className={cn('text-muted-foreground max-w-md', vs.description)}>
<Balancer>{description}</Balancer>
</p>
)
const featureElement = featureText && (
<a
href={featureHref}
className="text-foreground group inline-flex items-center gap-2 text-sm font-medium transition-[opacity,transform] hover:opacity-80 active:scale-[0.96]"
>
<span>{featureText}</span>
<ArrowRight className="size-3.5 transition-transform group-hover:translate-x-0.5" />
</a>
)
const studioElement = (
<div className={cn('relative mx-auto w-full', vs.studio)}>
<div
className={cn(
'relative w-full overflow-hidden',
'mask-r-from-90% mask-r-to-100%',
)}
>
<StudioDemo />
</div>
</div>
)
return (
<section className="relative isolate w-full overflow-hidden">
<motion.div
className={cn(
'relative z-10 mx-auto flex max-w-7xl flex-col px-6',
vs.section,
vs.content,
)}
variants={animate ? container : undefined}
initial={animate ? 'hidden' : false}
whileInView={animate ? 'visible' : undefined}
viewport={{ once: true, margin: '-80px' }}
>
<div className={cn('flex w-full flex-col', vs.header)}>
<Reveal active={animate} variants={titleItem}>
{titleElement}
</Reveal>
<div className="flex w-full flex-col gap-3 sm:flex-row sm:items-end sm:justify-between sm:gap-8">
<Reveal active={animate} variants={descriptionItem}>
{descriptionElement}
</Reveal>
<Reveal
active={animate}
variants={featureItem}
className="shrink-0"
>
{featureElement}
</Reveal>
</div>
</div>
<Reveal active={animate} variants={studioItem} className="w-full">
{studioElement}
</Reveal>
</motion.div>
</section>
)
}
registry/blocks/hero/hero-11/hero-11-example.tsx
import { Hero11, type Hero11Props } from './hero-11'
export const values = {
title: 'The interface system for teams and agents.',
description:
'Purpose-built blocks for planning and shipping product UI. Designed for the AI era.',
featureText: 'Compositions',
featureHref: '/compositions',
animation: 'subtle',
variant: 'standard',
} satisfies Hero11Props
export function Hero11Example() {
return <Hero11 {...values} />
}
registry/blocks/hero/hero-11/studio-demo.tsx
import type * as React from 'react'
import {
ArrowUp,
Boxes,
CircleDot,
Component,
FileText,
Layers,
LayoutTemplate,
MoreHorizontal,
Paperclip,
Plus,
Search,
SignalHigh,
Sparkles,
} from 'lucide-react'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { cn } from '@/lib/utils'
const nav = [
{ icon: Boxes, label: 'Catalog', count: '128' },
{ icon: LayoutTemplate, label: 'Blocks', count: '48' },
{ icon: Layers, label: 'Patterns' },
{ icon: Component, label: 'Compositions' },
]
const collections = [
{ label: 'Heroes' },
{ label: 'Forms' },
{ label: 'Illustrations' },
]
const recent = [
{ label: 'Split-copy hero', active: true },
{ label: 'Agent studio' },
{ label: 'Empty states' },
{ label: 'Auth screens' },
]
const activity = [
{
src: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=64&q=80',
name: 'Flex',
action: 'added to catalog',
time: '2m',
body: null as string | null,
},
{
src: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=64&q=80',
name: 'Maya',
action: 'tagged',
time: '6m',
body: null,
badge: 'Hero',
},
{
src: 'https://images.unsplash.com/photo-1633332755192-727a05c4013d?w=64&q=80',
name: 'Noah',
action: 'commented',
time: '4m',
body: 'Keep the right edge soft — sidebar stays crisp against the page.',
},
{
src: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=64&q=80',
name: 'Ava',
action: 'commented',
time: '3m',
body: 'Light-first. Soft shadows, concentric radii, tabular counts.',
},
]
const changedFiles = [
{ name: 'studio-demo.tsx', add: 48, del: 12 },
{ name: 'hero-11.tsx', add: 16, del: 4 },
]
export function StudioDemo() {
return (
<div className="relative w-full">
<div
className={cn(
'bg-card w-full overflow-hidden rounded-xl border',
'shadow-[10px_18px_40px_-18px_rgba(0,0,0,0.16)]',
'dark:shadow-[10px_18px_40px_-18px_rgba(0,0,0,0.5)]',
)}
>
<div className="flex min-h-[30rem]">
{/* Sidebar */}
<aside className="bg-card hidden w-52 shrink-0 flex-col border-r sm:flex">
<div className="flex items-center gap-2 px-3 py-3">
<div className="bg-foreground text-background flex size-5 items-center justify-center rounded-md text-[10px] font-semibold">
F
</div>
<span className="truncate text-sm font-semibold tracking-tight">
Flexnative
</span>
<div className="ml-auto flex items-center gap-0.5">
<Button
size="icon-xs"
variant="ghost"
className="text-muted-foreground"
aria-label="Search"
>
<Search className="size-3.5" />
</Button>
<Button
size="icon-xs"
variant="ghost"
className="text-muted-foreground"
aria-label="New block"
>
<Plus className="size-3.5" />
</Button>
</div>
</div>
<nav className="flex flex-col gap-0.5 px-2">
{nav.map(({ icon: Icon, label, count }) => (
<div
key={label}
className="text-muted-foreground hover:bg-muted flex items-center gap-2 rounded-md px-2 py-1.5 text-xs"
>
<Icon className="size-3.5 shrink-0" />
<span className="min-w-0 flex-1 truncate">{label}</span>
{count && (
<span className="text-muted-foreground/80 tabular-nums">
{count}
</span>
)}
</div>
))}
</nav>
<div className="mt-4 px-2">
<span className="text-muted-foreground px-2 text-[10px] font-medium tracking-wide uppercase">
Collections
</span>
<div className="mt-1 flex flex-col gap-0.5">
{collections.map(({ label }) => (
<div
key={label}
className="text-muted-foreground hover:bg-muted truncate rounded-md px-2 py-1.5 text-xs"
>
{label}
</div>
))}
</div>
</div>
<div className="mt-4 flex-1 px-2 pb-3">
<span className="text-muted-foreground px-2 text-[10px] font-medium tracking-wide uppercase">
Recent
</span>
<div className="mt-1 flex flex-col gap-0.5">
{recent.map(({ label, active }) => (
<div
key={label}
className={cn(
'truncate rounded-md px-2 py-1.5 text-xs',
active
? 'bg-muted text-foreground font-medium'
: 'text-muted-foreground hover:bg-muted',
)}
>
{label}
</div>
))}
</div>
</div>
</aside>
{/* Main */}
<div className="flex min-w-0 flex-1 flex-col">
<header className="flex items-center gap-2 border-b px-3 py-2.5 sm:px-4">
<span className="truncate text-sm font-medium tracking-tight">
Split-copy hero
</span>
<div className="text-muted-foreground ml-auto flex items-center gap-1.5 text-[10px] tabular-nums">
<span>02</span>
<span className="text-muted-foreground/50">/</span>
<span>128</span>
</div>
</header>
<div className="flex min-h-0 flex-1">
<div className="flex min-w-0 flex-1 flex-col gap-4 p-4 sm:p-5">
<div className="space-y-2">
<h2 className="text-xl font-semibold tracking-tight sm:text-2xl">
Split-copy hero
</h2>
<p className="text-muted-foreground max-w-xl text-xs leading-relaxed text-pretty sm:text-[13px]">
A product studio mock with split copy, concentric radii, and
a soft{' '}
<code className="bg-muted rounded px-1 py-0.5 font-mono text-[11px]">
mask-*
</code>{' '}
fade so the interface dissolves into the page.
</p>
</div>
<Separator />
<div className="flex flex-col gap-3">
{activity.map((item, i) => (
<div key={`${item.name}-${i}`} className="flex gap-2.5">
<Avatar className="mt-0.5 size-6 shrink-0">
<AvatarImage src={item.src} alt="" />
<AvatarFallback className="text-[9px]">
{item.name[0]}
</AvatarFallback>
</Avatar>
<div className="min-w-0 flex-1 space-y-1.5">
<div className="flex flex-wrap items-baseline gap-x-1.5 gap-y-0.5 text-xs">
<span className="font-medium">{item.name}</span>
<span className="text-muted-foreground">
{item.action}
</span>
{'badge' in item && item.badge && (
<Badge
variant="secondary"
className="h-4 px-1.5 text-[9px]"
>
{item.badge}
</Badge>
)}
<span className="text-muted-foreground ml-auto text-[10px] tabular-nums">
{item.time}
</span>
</div>
{item.body && (
<div className="bg-muted/50 rounded-lg px-3 py-2 text-xs leading-relaxed text-pretty">
{item.body}
</div>
)}
</div>
</div>
))}
<div className="flex items-center gap-2.5 text-xs">
<div className="flex size-6 shrink-0 items-center justify-center rounded-full border border-amber-400/40 bg-amber-400/10">
<CircleDot className="size-3 text-amber-500" />
</div>
<span className="text-muted-foreground">
Moved from{' '}
<span className="text-foreground font-medium">Draft</span>{' '}
to{' '}
<span className="text-foreground font-medium">
In review
</span>
</span>
<span className="text-muted-foreground ml-auto text-[10px] tabular-nums">
1m
</span>
</div>
</div>
</div>
{/* Properties */}
<aside className="hidden w-40 shrink-0 flex-col gap-3 border-l p-3 md:flex lg:w-44">
<Property
label="Status"
value={
<span className="flex items-center gap-1.5">
<span className="size-1.5 rounded-full bg-amber-400" />
In review
</span>
}
/>
<Property
label="Priority"
value={
<span className="flex items-center gap-1.5">
<SignalHigh className="size-3.5 text-orange-500" />
High
</span>
}
/>
<Property
label="Owner"
value={
<span className="flex items-center gap-1.5">
<Avatar className="size-4">
<AvatarImage
src="https://images.unsplash.com/photo-1633332755192-727a05c4013d?w=64&q=80"
alt=""
/>
<AvatarFallback className="text-[8px]">
N
</AvatarFallback>
</Avatar>
Noah
</span>
}
/>
<Property
label="Category"
value={
<span className="flex items-center gap-1.5">
<span className="bg-foreground/90 size-3.5 rounded-[4px]" />
Hero
</span>
}
/>
<Property
label="Slug"
value={<span className="font-mono text-[11px]">hero-11</span>}
/>
</aside>
</div>
</div>
</div>
</div>
{/* Floating agent panel */}
<div
className={cn(
'bg-card absolute right-2 bottom-3 z-10 w-[min(100%-1rem,17.5rem)] overflow-hidden rounded-xl sm:right-4 sm:w-72',
'shadow-[0_10px_32px_-8px_rgba(0,0,0,0.2)]',
'dark:shadow-[0_10px_32px_-8px_rgba(0,0,0,0.55)]',
)}
>
<div className="flex items-center gap-2 border-b px-3 py-2">
<Sparkles className="text-foreground size-3.5 shrink-0" />
<span className="truncate text-xs font-medium tracking-tight">
Flex Agent
</span>
<Badge
variant="secondary"
className="h-4 border-emerald-500/20 bg-emerald-500/10 px-1.5 text-[9px] text-emerald-700 dark:text-emerald-400"
>
Live
</Badge>
<Button
size="icon-xs"
variant="ghost"
className="text-muted-foreground ml-auto"
aria-label="More"
>
<MoreHorizontal className="size-3.5" />
</Button>
</div>
<div className="space-y-2.5 px-3 py-2.5">
<p className="text-muted-foreground text-[11px] leading-relaxed">
Examining the hero mask path…
</p>
<p className="text-muted-foreground text-[10px] tabular-nums">
Worked for 7s
</p>
<p className="text-[11px] leading-relaxed">
Pushed and opened a draft PR.
</p>
<div className="bg-muted/60 space-y-1.5 rounded-lg p-2">
<div className="flex items-center justify-between gap-2">
<span className="text-[10px] font-medium">
Changed 2 files{' '}
<span className="text-emerald-600 tabular-nums dark:text-emerald-400">
+64
</span>{' '}
<span className="text-red-500 tabular-nums dark:text-red-400">
-16
</span>
</span>
<Button
size="xs"
variant="outline"
className="h-5 px-1.5 text-[9px]"
>
Preview
</Button>
</div>
{changedFiles.map((f) => (
<div
key={f.name}
className="text-muted-foreground flex items-center gap-1.5 text-[10px]"
>
<FileText className="size-3 shrink-0" />
<span className="min-w-0 flex-1 truncate font-mono">
{f.name}
</span>
<span className="text-emerald-600 tabular-nums dark:text-emerald-400">
+{f.add}
</span>
<span className="text-red-500 tabular-nums dark:text-red-400">
-{f.del}
</span>
</div>
))}
</div>
</div>
<div className="border-t p-2">
<div className="bg-muted/50 flex items-center gap-1.5 rounded-lg px-2 py-1.5">
<span className="text-muted-foreground min-w-0 flex-1 truncate text-[11px]">
Tell Flex what to do next…
</span>
<Button
size="icon-xs"
variant="ghost"
className="text-muted-foreground"
aria-label="Attach"
>
<Paperclip className="size-3.5" />
</Button>
<Button
size="icon-xs"
variant="default"
className="size-6 rounded-md active:scale-[0.96]"
aria-label="Send"
>
<ArrowUp className="size-3.5" />
</Button>
</div>
</div>
</div>
</div>
)
}
function Property({
label,
value,
}: Readonly<{ label: string; value: React.ReactNode }>) {
return (
<div className="space-y-1">
<span className="text-muted-foreground text-[10px]">{label}</span>
<div className="text-foreground text-xs font-medium">{value}</div>
</div>
)
}