heroes /
Blob Portrait Hero (Editorial Magazine)
Editorial fashion/blogger hero with cream bg, italic Cormorant Garamond serif headline, body copy, yellow CTA pill, and a blob-shaped portrait on the right (with a softer yellow blob behind it). Three yellow circle accents bleed off the corners. Follow + social links footer. Built from a
Preview
Source
tsx
import { Twitter, Facebook, Menu, Share2 } from "lucide-react";
export interface BlobPortraitHeroProps {
brand?: string;
navItems?: { label: string; href: string }[];
headlineLines?: string[];
body?: string;
primaryCta?: { label: string; href: string };
followLabel?: string;
socialLinks?: { type: "twitter" | "facebook" | "instagram"; href: string }[];
imageSrc?: string;
imageAlt?: string;
/** Default = Ladie cream bg #fefcf8 */
bgColor?: string;
/** Default = Ladie warm yellow #fcc638 */
accentYellow?: string;
/** Default = Ladie deep ink #18171c */
inkColor?: string;
}
export default function BlobPortraitHero({
brand = "ladie",
navItems = [
{ label: "About Us", href: "#about" },
{ label: "Blog", href: "#blog" },
{ label: "Contact Us", href: "#contact" },
],
headlineLines = [
"Be a girl with a mind",
"A woman with attitude",
"& a lady with class",
],
body = "Fashion is a language which tells a story about the person who wears it. “Clothes create a wordless means of communication that we all understand.”",
primaryCta = { label: "Read more", href: "#read" },
followLabel = "Follow Us",
socialLinks = [
{ type: "twitter", href: "#" },
{ type: "facebook", href: "#" },
],
imageSrc = "/heroes/blob-portrait/woman.webp",
imageAlt = "Featured editorial portrait",
bgColor = "#fefcf8",
accentYellow = "#fcc638",
inkColor = "#18171c",
}: BlobPortraitHeroProps) {
return (
<>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400..700;1,400..700&family=Inter:wght@400;500;600&display=swap"
/>
<section
className="relative min-h-[680px] overflow-hidden"
style={{ background: bgColor, fontFamily: "'Inter', sans-serif", color: inkColor }}
>
{/* Decorative yellow circles */}
<div
aria-hidden
className="absolute -left-32 -top-20 h-72 w-72 rounded-full"
style={{ background: accentYellow, opacity: 0.85 }}
/>
<div
aria-hidden
className="absolute -right-24 -top-24 h-56 w-56 rounded-full"
style={{ background: accentYellow, opacity: 0.85 }}
/>
<div
aria-hidden
className="absolute -left-10 bottom-[-120px] h-72 w-72 rounded-full"
style={{ background: accentYellow, opacity: 0.85 }}
/>
<div className="relative mx-auto max-w-7xl px-6 sm:px-10 lg:px-14">
{/* Nav */}
<nav className="flex items-center justify-between pt-6">
<a href="/" className="flex items-center gap-2">
<span
className="text-2xl font-bold italic"
style={{ fontFamily: "'Cormorant Garamond', serif", color: inkColor }}
>
{brand}
</span>
</a>
<div className="hidden items-center gap-10 md:flex">
{navItems.map((item) => (
<a
key={item.label}
href={item.href}
className="text-sm transition-opacity hover:opacity-60"
style={{ color: inkColor }}
>
{item.label}
</a>
))}
</div>
<button
type="button"
aria-label="Open menu"
className="rounded-md p-2 transition-opacity hover:opacity-60"
style={{ color: inkColor }}
>
<Menu className="h-5 w-5" />
</button>
</nav>
{/* Hero grid */}
<div className="grid items-center gap-10 py-12 lg:grid-cols-[1fr_1.05fr] lg:gap-12 lg:py-16">
{/* Left: copy */}
<div className="relative z-10 max-w-xl pl-2 sm:pl-6">
<h1
className="font-bold leading-[1.15] tracking-tight"
style={{
fontFamily: "'Cormorant Garamond', serif",
fontSize: "clamp(2.25rem, 4.4vw, 3.25rem)",
color: inkColor,
}}
>
{headlineLines.map((line, i) => (
<span key={i} className="block">
{line}
</span>
))}
</h1>
<p
className="mt-6 max-w-sm text-sm leading-relaxed"
style={{ color: "#5b5763" }}
>
{body}
</p>
<a
href={primaryCta.href}
className="mt-7 inline-flex items-center justify-center rounded-md px-7 py-3 text-sm font-semibold transition-all hover:-translate-y-0.5"
style={{
background: accentYellow,
color: inkColor,
boxShadow: `0 6px 20px ${accentYellow}55`,
}}
>
{primaryCta.label}
</a>
</div>
{/* Right: blob-shaped photo */}
<div className="relative">
{/* Yellow blob accent behind */}
<div
aria-hidden
className="absolute inset-[2%] -left-[6%] -bottom-[4%]"
style={{
background: accentYellow,
borderRadius: "65% 35% 50% 50% / 55% 45% 55% 45%",
opacity: 0.95,
}}
/>
{/* Photo in blob frame */}
<div
className="relative overflow-hidden"
style={{
borderRadius: "55% 45% 60% 40% / 50% 60% 40% 50%",
aspectRatio: "5/4",
boxShadow: "0 20px 60px rgba(0,0,0,0.08)",
}}
>
<img
src={imageSrc}
alt={imageAlt}
width={900}
height={720}
loading="eager"
fetchPriority="high"
decoding="async"
className="h-full w-full object-cover"
/>
</div>
</div>
</div>
{/* Footer row: Follow + socials */}
<div className="flex items-center justify-between pb-10 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm font-semibold" style={{ color: inkColor }}>
{followLabel}
</span>
<div className="flex items-center gap-3">
{socialLinks.map((s, i) => (
<a
key={i}
href={s.href}
aria-label={s.type}
className="transition-opacity hover:opacity-60"
style={{ color: inkColor }}
>
{s.type === "twitter" && <Twitter className="h-4 w-4" />}
{s.type === "facebook" && <Facebook className="h-4 w-4" />}
</a>
))}
</div>
</div>
<a
href="#share"
aria-label="Share"
className="transition-opacity hover:opacity-60"
style={{ color: inkColor }}
>
<Share2 className="h-5 w-5" />
</a>
</div>
</div>
</section>
</>
);
} Claude Code Instructions
CLI Install
npx innovations add blob-portraitWhere to use it
Use this for editorial / lifestyle / fashion / personal-brand bloggers — anywhere you've got a strong portrait and a thoughtful, considered voice.
In Astro:
---
import BlobPortraitHero from '../components/innovations/heroes/blob-portrait';
---
<BlobPortraitHero />
In Next.js:
import BlobPortraitHero from '@/components/innovations/heroes/blob-portrait';
No client:* needed — server-renderable.
CUSTOMIZATION:
<BlobPortraitHero
brand="yourname"
navItems={[{ label: "About", href: "/about" }, ...]}
headlineLines={["First line", "Second line", "Third line"]}
body="Body paragraph..."
primaryCta={{ label: "Read more", href: "/read" }}
socialLinks={[
{ type: "twitter", href: "#" },
{ type: "facebook", href: "#" },
{ type: "instagram", href: "#" },
]}
imageSrc="/your-portrait.webp"
bgColor="#fefcf8"
accentYellow="#fcc638"
inkColor="#18171c"
/>
PALETTE: cream + warm yellow + ink. Easy to rebrand by swapping the 3 color props.
BLOB SHAPES: the photo and its accent shadow both use organic border-radius percentages. Adjust the radii in the JSX (look for "borderRadius: '55% 45% 60% 40% / 50% 60% 40% 50%'") to get a different blob silhouette.
DECORATIVE CIRCLES: three yellow circles bleed off corners (top-left, top-right, bottom-left). Tune their size or remove them by editing the three "Decorative yellow circles" divs.
FONT: Cormorant Garamond + Inter, shipped via baked-in Google Fonts <link>.