nexus-labs
componentblog-content-sectionssource: stale · stale
Blog Content Sections
Centered article, table-of-contents, editorial, featured post, and hover-card blog sections.
npx shadcn add "https://nexus-ui.com/r/blog-content-sections.json"free
This source snapshot is older than our freshness window and couldn't be re-verified just now — it may be outdated.
Where it goes
data-tableblog-listfeature-grid
Use cases
dashboard
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · mediumanimationframer-motion
iconslucide-react
Registry dependencies
https://www.nexus-ui.com/api/registry/nexus-fontutils
Files6 · source cached 10/7/2026, 9:07:40 PM
next-ui/blocks/blog-content-sections/blog-content-centered.tsx
"use client";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
export type BlogContentCenteredProps = {
className?: string;
title?: string;
author?: string;
date?: string;
heroImage?: string;
secondaryImage?: string;
sectionIdPrefix?: string;
};
const defaultHeroImage =
"https://images.unsplash.com/photo-1593508512255-86ab42a8e620?auto=format&fit=crop&w=1600&q=80";
const defaultSecondaryImage =
"https://images.unsplash.com/photo-1518770660439-4636190af475?auto=format&fit=crop&w=1400&q=80";
export function BlogContentCentered({
className,
title = "The Future of Artificial Intelligence",
author = "Ava Sterling",
date = "February 14, 2026",
heroImage = defaultHeroImage,
secondaryImage = defaultSecondaryImage,
sectionIdPrefix = "blog-centered",
}: BlogContentCenteredProps) {
return (
<section
className={cn(
"min-h-full w-full overflow-y-auto bg-[#f7f7f7] px-5 py-12 text-zinc-950 dark:bg-zinc-950 dark:text-white sm:px-8 lg:py-20",
className,
)}
>
<motion.article
initial={{ opacity: 0, y: 18 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.55, ease: [0.22, 1, 0.36, 1] }}
className="mx-auto w-full max-w-[620px]"
>
<HeroImage image={heroImage} className="aspect-[1.38/1]" />
<header className="mt-7">
<h1 className="text-3xl font-semibold tracking-tight text-zinc-950 dark:text-white sm:text-4xl">
{title}
</h1>
<div className="mt-4 flex items-center gap-3 text-sm text-zinc-500 dark:text-zinc-400">
<span className="grid size-6 place-items-center rounded-full bg-gradient-to-br from-sky-400 to-violet-500 text-[10px] font-bold text-white">
AS
</span>
<span>{author}</span>
<span className="size-1 rounded-full bg-zinc-300 dark:bg-zinc-700" />
<time>{date}</time>
</div>
</header>
<ArticleBody secondaryImage={secondaryImage} sectionIdPrefix={sectionIdPrefix} />
</motion.article>
</section>
);
}
function ArticleBody({
secondaryImage,
sectionIdPrefix,
}: {
secondaryImage: string;
sectionIdPrefix: string;
}) {
return (
<div className="mt-12 space-y-10 text-[15px] leading-8 text-zinc-600 dark:text-zinc-300 sm:text-base sm:leading-8">
<section id={`${sectionIdPrefix}-introduction`} className="scroll-mt-10">
<h2 className="text-xl font-semibold tracking-tight text-zinc-950 dark:text-white sm:text-2xl">
Introduction
</h2>
<p className="mt-4">
Artificial intelligence is moving from experimental demos into the
quiet parts of daily work. It drafts, reviews, summarizes, designs,
and helps teams make better decisions with less context switching.
</p>
</section>
<section id={`${sectionIdPrefix}-key-areas`} className="scroll-mt-10">
<h2 className="text-xl font-semibold tracking-tight text-zinc-950 dark:text-white sm:text-2xl">
Key Areas of AI Development
</h2>
<ol className="mt-5 list-decimal space-y-5 pl-5">
<li>
<strong className="font-semibold text-zinc-900 dark:text-white">Machine learning</strong>
<ul className="mt-3 list-disc space-y-1 pl-5 text-sm text-zinc-500 dark:text-zinc-400">
<li>Deep learning for image, text, and audio reasoning</li>
<li>Reinforcement systems that improve through feedback</li>
<li>Recommendation engines for personalized experiences</li>
</ul>
</li>
<li>
<strong className="font-semibold text-zinc-900 dark:text-white">Natural language processing</strong>
<ul className="mt-3 list-disc space-y-1 pl-5 text-sm text-zinc-500 dark:text-zinc-400">
<li>Conversation interfaces for support and research</li>
<li>Semantic search across private knowledge bases</li>
<li>Language translation for global product teams</li>
</ul>
</li>
<li>
<strong className="font-semibold text-zinc-900 dark:text-white">Computer vision</strong>
<ul className="mt-3 list-disc space-y-1 pl-5 text-sm text-zinc-500 dark:text-zinc-400">
<li>Visual inspection for manufacturing and healthcare</li>
<li>Spatial awareness for robotics and mobility</li>
<li>Creative tooling for media generation</li>
</ul>
</li>
</ol>
</section>
<section id={`${sectionIdPrefix}-ethics`} className="scroll-mt-10">
<h2 className="text-xl font-semibold tracking-tight text-zinc-950 dark:text-white sm:text-2xl">
Ethical Considerations
</h2>
<p className="mt-4">
The most useful systems are also the most accountable. Privacy,
fairness, explainability, and human oversight should be designed into
AI products from the first prototype rather than added at launch.
</p>
<blockquote className="mt-6 border-l-2 border-zinc-950 pl-5 text-sm font-medium italic text-zinc-800 dark:border-white dark:text-zinc-200">
“The value of AI is not speed alone. It is the quality of judgment it
helps people bring to complex work.”
</blockquote>
</section>
<HeroImage image={secondaryImage} className="aspect-[1.55/1]" />
<section id={`${sectionIdPrefix}-conclusion`} className="scroll-mt-10">
<h2 className="text-xl font-semibold tracking-tight text-zinc-950 dark:text-white sm:text-2xl">
Conclusion
</h2>
<p className="mt-4">
AI will keep reshaping software, but the strongest products will
remain deeply human: clear, trustworthy, calm under pressure, and easy
to understand when the stakes are high.
</p>
<p className="mt-5 text-sm">
For more research notes, visit{" "}
<a href="#" className="font-semibold text-zinc-950 underline underline-offset-4 dark:text-white">
Nexus Research Center
</a>
.
</p>
</section>
</div>
);
}
function HeroImage({ image, className }: { image: string; className?: string }) {
return (
<div
className={cn(
"overflow-hidden rounded-3xl border border-zinc-200 bg-cover bg-center shadow-2xl shadow-zinc-950/8 dark:border-white/10 dark:shadow-black/30",
className,
)}
style={{ backgroundImage: `url('${image}')` }}
role="img"
aria-label="Editorial article image"
>
<div className="h-full w-full bg-gradient-to-tr from-zinc-950/12 via-transparent to-white/12" />
</div>
);
}
export default BlogContentCentered;
next-ui/blocks/blog-content-sections/blog-content-editorial.tsx
"use client";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
export type BlogContentEditorialProps = {
className?: string;
};
const image =
"https://images.unsplash.com/photo-1551288049-bebda4e38f71?auto=format&fit=crop&w=1600&q=80";
export function BlogContentEditorial({ className }: BlogContentEditorialProps) {
return (
<section
className={cn(
"min-h-full w-full overflow-y-auto bg-zinc-950 px-5 py-10 text-white sm:px-8 lg:px-10 lg:py-14",
className,
)}
>
<article className="mx-auto grid w-full max-w-7xl gap-8 lg:min-h-[760px] lg:grid-cols-[0.9fr_1.1fr] lg:items-stretch">
<motion.header
initial={{ opacity: 0, y: 18 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.55 }}
className="relative overflow-hidden rounded-[2rem] border border-white/10 bg-white/[0.04] p-6 shadow-2xl shadow-black/30 sm:p-8 lg:p-10"
>
<div className="absolute inset-0 bg-[radial-gradient(circle_at_20%_16%,rgba(124,58,237,0.24),transparent_32%),radial-gradient(circle_at_82%_76%,rgba(14,165,233,0.20),transparent_34%)]" />
<div className="relative flex h-full flex-col justify-between">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-violet-200/70">
Nexus Field Notes
</p>
<h1 className="mt-6 max-w-xl text-4xl font-semibold tracking-tight text-white sm:text-5xl lg:text-6xl">
Designing AI products people can actually trust.
</h1>
<p className="mt-6 max-w-lg text-base leading-7 text-white/58 sm:text-lg sm:leading-8">
A practical editorial layout for opinion pieces, product essays,
founder notes, and long-form launch stories.
</p>
</div>
<div className="mt-10 grid gap-3 sm:grid-cols-3">
{[
["8 min", "read time"],
["Field", "research"],
["2026", "edition"],
].map(([value, label]) => (
<div key={label} className="rounded-2xl border border-white/10 bg-white/[0.06] p-4">
<p className="text-lg font-semibold">{value}</p>
<p className="mt-1 text-xs uppercase tracking-[0.18em] text-white/38">{label}</p>
</div>
))}
</div>
</div>
</motion.header>
<div className="overflow-hidden rounded-[2rem] border border-white/10 bg-white text-zinc-950 shadow-2xl shadow-black/30 dark:bg-zinc-900 dark:text-white">
<div
className="h-72 bg-cover bg-center sm:h-96"
style={{ backgroundImage: `url('${image}')` }}
role="img"
aria-label="Analytics dashboard and product research"
>
<div className="h-full w-full bg-gradient-to-t from-zinc-950/45 via-transparent to-transparent" />
</div>
<div className="space-y-8 p-6 sm:p-8 lg:p-10">
<section>
<h2 className="text-2xl font-semibold tracking-tight">Start with clarity, not magic.</h2>
<p className="mt-4 leading-8 text-zinc-600 dark:text-zinc-300">
Teams adopt AI fastest when the interface explains what happened,
what changed, and how a person can correct the outcome. The best
systems feel powerful without becoming mysterious.
</p>
</section>
<section className="grid gap-4 sm:grid-cols-2">
{[
["Readable outputs", "Structure generated responses so people can scan, verify, and act."],
["Visible controls", "Let users steer tone, depth, data sources, and confidence."],
["Human checkpoints", "Build moments where review is natural instead of disruptive."],
["Calm recovery", "When the model is wrong, the interface should make correction easy."],
].map(([title, text]) => (
<div key={title} className="rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-white/10 dark:bg-white/5">
<h3 className="font-semibold">{title}</h3>
<p className="mt-2 text-sm leading-6 text-zinc-500 dark:text-zinc-400">{text}</p>
</div>
))}
</section>
<blockquote className="rounded-2xl border border-zinc-200 bg-zinc-50 p-5 text-lg font-medium leading-8 dark:border-white/10 dark:bg-white/5">
“Trust is designed in layers: source, control, explanation, and a
graceful way back when the output misses.”
</blockquote>
</div>
</div>
</article>
</section>
);
}
export default BlogContentEditorial;
next-ui/blocks/blog-content-sections/blog-content-with-toc.tsx
"use client";
import { motion } from "framer-motion";
import { cn } from "@/lib/utils";
import { BlogContentCentered } from "./blog-content-centered";
export type BlogContentWithTocProps = {
className?: string;
};
const tocItems = [
{ id: "introduction", label: "Introduction" },
{ id: "key-areas", label: "Key Areas of AI Development" },
{ id: "ethics", label: "Ethical Considerations" },
{ id: "conclusion", label: "Conclusion" },
];
export function BlogContentWithToc({ className }: BlogContentWithTocProps) {
const sectionIdPrefix = "blog-with-toc";
return (
<section
className={cn(
"min-h-full w-full overflow-y-auto bg-[#f7f7f7] text-zinc-950 dark:bg-zinc-950 dark:text-white",
className,
)}
>
<div className="mx-auto grid w-full max-w-6xl gap-8 px-5 py-10 sm:px-8 lg:grid-cols-[250px_minmax(0,760px)] lg:px-10 lg:py-20">
<aside className="hidden lg:block">
<motion.nav
initial={{ opacity: 0, x: -12 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.45, ease: [0.22, 1, 0.36, 1] }}
className="sticky top-10 space-y-1 text-sm text-zinc-500 dark:text-zinc-400"
aria-label="Table of contents"
>
{tocItems.map((item, index) => (
<a
key={item.id}
href={`#${sectionIdPrefix}-${item.id}`}
className={cn(
"block rounded-xl px-3 py-2 transition hover:bg-white hover:text-zinc-950 hover:shadow-sm dark:hover:bg-white/8 dark:hover:text-white",
index === 1 && "border border-zinc-300 text-zinc-950 dark:border-white/25 dark:text-white",
)}
>
{item.label}
</a>
))}
</motion.nav>
</aside>
<BlogContentCentered
className="min-h-0 overflow-visible bg-transparent px-0 py-0"
sectionIdPrefix={sectionIdPrefix}
/>
</div>
</section>
);
}
export default BlogContentWithToc;
next-ui/blocks/blog-content-sections/blog-header-featured-posts.tsx
"use client";
import * as React from "react";
import { ArrowLeft, ArrowRight, ArrowUpRight, ChevronDown } from "lucide-react";
import { cn } from "@/lib/utils";
type ArticleTag = {
name: string;
tone: "violet" | "blue" | "emerald" | "rose" | "amber" | "slate";
};
type Article = {
id: string;
title: string;
summary: string;
category: string;
thumbnailUrl: string;
publishedAt: string;
readingTime: string;
author: {
name: string;
initials: string;
avatarUrl: string;
};
tags: ArticleTag[];
};
export type BlogHeaderFeaturedPostsProps = {
className?: string;
eyebrow?: string;
title?: string;
description?: string;
};
const tabs = [
{ id: "all", label: "View all" },
{ id: "systems", label: "Systems" },
{ id: "motion", label: "Motion" },
{ id: "frontend", label: "Frontend" },
{ id: "launch", label: "Launch" },
];
const sortOptions = ["Most recent", "Most popular", "Most viewed"];
const featuredArticle: Article = {
id: "featured",
title: "Design systems that make launches feel effortless",
summary:
"A practical look at how reusable sections, motion rules, and clean visual language help teams ship polished product pages without rebuilding the same pieces.",
category: "Systems",
thumbnailUrl:
"https://images.unsplash.com/photo-1518005020951-eccb494ad742?auto=format&fit=crop&w=1800&q=80",
publishedAt: "22 May 2026",
readingTime: "10 min read",
author: {
name: "Ariana Reed",
initials: "AR",
avatarUrl:
"https://images.unsplash.com/photo-1531123897727-8f129e1688ce?auto=format&fit=crop&w=160&q=80",
},
tags: [
{ name: "Systems", tone: "slate" },
{ name: "Components", tone: "slate" },
{ name: "Launch", tone: "slate" },
],
};
const articles: Article[] = [
{
id: "article-1",
title: "A field guide to reusable hero sections",
summary: "How to compose headlines, CTAs, visuals, and trust signals into hero blocks that actually convert.",
category: "Systems",
thumbnailUrl:
"https://images.unsplash.com/photo-1559028012-481c04fa702d?auto=format&fit=crop&w=900&q=80",
publishedAt: "21 May 2026",
readingTime: "7 min read",
author: {
name: "Nina Kapoor",
initials: "NK",
avatarUrl:
"https://images.unsplash.com/photo-1544005313-94ddf0286df2?auto=format&fit=crop&w=160&q=80",
},
tags: [
{ name: "Hero", tone: "violet" },
{ name: "Conversion", tone: "blue" },
{ name: "UX", tone: "rose" },
],
},
{
id: "article-2",
title: "Motion rules for premium SaaS pages",
summary: "A simple system for entrances, hover states, scroll effects, and animation timing that feels expensive.",
category: "Motion",
thumbnailUrl:
"https://images.unsplash.com/photo-1633356122544-f134324a6cee?auto=format&fit=crop&w=900&q=80",
publishedAt: "20 May 2026",
readingTime: "9 min read",
author: {
name: "Ishan Mehta",
initials: "IM",
avatarUrl:
"https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?auto=format&fit=crop&w=160&q=80",
},
tags: [
{ name: "Framer Motion", tone: "blue" },
{ name: "Timing", tone: "rose" },
],
},
{
id: "article-3",
title: "Shipping components with better props",
summary: "The prop patterns that make UI components easier to reuse, customize, and publish.",
category: "Frontend",
thumbnailUrl:
"https://images.unsplash.com/photo-1555066931-4365d14bab8c?auto=format&fit=crop&w=900&q=80",
publishedAt: "19 May 2026",
readingTime: "8 min read",
author: {
name: "Theo Banerjee",
initials: "TB",
avatarUrl:
"https://images.unsplash.com/photo-1527980965255-d3b416303d12?auto=format&fit=crop&w=160&q=80",
},
tags: [
{ name: "React", tone: "emerald" },
{ name: "Props", tone: "slate" },
],
},
{
id: "article-4",
title: "The landing page audit checklist",
summary: "A focused review pass for spacing, contrast, hierarchy, responsiveness, and copy clarity.",
category: "Launch",
thumbnailUrl:
"https://images.unsplash.com/photo-1553877522-43269d4ea984?auto=format&fit=crop&w=900&q=80",
publishedAt: "18 May 2026",
readingTime: "6 min read",
author: {
name: "Maya Sen",
initials: "MS",
avatarUrl:
"https://images.unsplash.com/photo-1489424731084-a5d8b219a5bb?auto=format&fit=crop&w=160&q=80",
},
tags: [
{ name: "Checklist", tone: "violet" },
{ name: "Launch", tone: "slate" },
],
},
{
id: "article-5",
title: "Designing bento grids that do real work",
summary: "How to avoid random card layouts and build bento sections with rhythm, hierarchy, and purpose.",
category: "Systems",
thumbnailUrl:
"https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?auto=format&fit=crop&w=900&q=80",
publishedAt: "17 May 2026",
readingTime: "7 min read",
author: {
name: "Rhea Thomas",
initials: "RT",
avatarUrl:
"https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=160&q=80",
},
tags: [
{ name: "Bento", tone: "blue" },
{ name: "Layout", tone: "amber" },
],
},
{
id: "article-6",
title: "Making docs pages feel like product",
summary: "Navigation, examples, code previews, and copy details that help users trust a component library.",
category: "Frontend",
thumbnailUrl:
"https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&w=900&q=80",
publishedAt: "16 May 2026",
readingTime: "5 min read",
author: {
name: "Dev Arora",
initials: "DA",
avatarUrl:
"https://images.unsplash.com/photo-1519085360753-af0119f7cbe7?auto=format&fit=crop&w=160&q=80",
},
tags: [
{ name: "Docs", tone: "violet" },
{ name: "DX", tone: "blue" },
],
},
];
export function BlogHeaderFeaturedPosts({
className,
eyebrow = "Nexus journal",
title = "Design notes for faster launches",
description = "Practical writing on component systems, polished motion, frontend craft, and launch-ready product pages.",
}: BlogHeaderFeaturedPostsProps) {
const [activeTab, setActiveTab] = React.useState("all");
const [sortBy, setSortBy] = React.useState(sortOptions[0]);
return (
<section
className={cn(
"min-h-full w-full overflow-y-auto bg-white px-5 py-12 text-zinc-950 dark:bg-zinc-950 dark:text-white sm:px-8 lg:px-10 lg:py-18",
className,
)}
>
<div className="mx-auto max-w-7xl">
<header className="max-w-3xl">
<p className="text-sm font-semibold text-violet-600 dark:text-violet-300">{eyebrow}</p>
<h1 className="mt-3 text-4xl font-semibold tracking-tight sm:text-5xl lg:text-6xl">
{title}
</h1>
<p className="mt-5 max-w-2xl text-lg leading-8 text-zinc-600 dark:text-zinc-400">
{description}
</p>
</header>
<FeaturedArticle article={featuredArticle} />
<div className="mt-12 flex flex-col gap-5 md:flex-row md:items-end md:justify-between">
<div className="flex gap-1 overflow-x-auto border-b border-zinc-200 dark:border-white/10">
{tabs.map((tab) => (
<button
key={tab.id}
type="button"
onClick={() => setActiveTab(tab.id)}
className={cn(
"whitespace-nowrap border-b-2 px-1.5 pb-3 pr-4 text-sm font-semibold transition",
activeTab === tab.id
? "border-zinc-950 text-zinc-950 dark:border-white dark:text-white"
: "border-transparent text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white",
)}
>
{tab.label}
</button>
))}
</div>
<label className="relative w-full md:max-w-48">
<span className="sr-only">Sort articles</span>
<select
value={sortBy}
onChange={(event) => setSortBy(event.target.value)}
className="h-11 w-full appearance-none rounded-xl border border-zinc-200 bg-white px-3.5 pr-10 text-sm font-medium text-zinc-800 outline-none transition focus:border-violet-400 focus:ring-4 focus:ring-violet-500/10 dark:border-white/10 dark:bg-white/5 dark:text-white"
>
{sortOptions.map((option) => (
<option key={option}>{option}</option>
))}
</select>
<ChevronDown className="pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-zinc-400" />
</label>
</div>
<ul className="mt-10 grid grid-cols-1 gap-x-8 gap-y-12 md:grid-cols-2 lg:grid-cols-3">
{articles.map((article) => (
<li key={article.id}>
<ArticleCard article={article} />
</li>
))}
</ul>
<Pagination />
</div>
</section>
);
}
function FeaturedArticle({ article }: { article: Article }) {
return (
<a
href="#"
className="group relative mt-14 hidden min-h-[560px] overflow-hidden rounded-[2rem] border border-zinc-200 bg-zinc-100 shadow-2xl shadow-zinc-950/8 outline-none transition focus-visible:ring-4 focus-visible:ring-violet-500/20 dark:border-white/10 dark:bg-white/5 md:block lg:min-h-[640px]"
>
<div
className="absolute inset-0 bg-cover bg-center transition duration-700 group-hover:scale-105"
style={{ backgroundImage: `url('${article.thumbnailUrl}')` }}
/>
<div className="absolute inset-0 bg-gradient-to-t from-zinc-950/72 via-zinc-950/8 to-transparent" />
<div className="absolute inset-x-0 bottom-0 p-6 text-white sm:p-8 lg:p-10">
<div className="flex flex-col gap-7">
<div>
<div className="flex gap-5">
<h2 className="flex-1 text-3xl font-semibold tracking-tight sm:text-4xl lg:text-5xl">
{article.title}
</h2>
<ArrowUpRight className="mt-2 size-7 shrink-0 transition group-hover:translate-x-1 group-hover:-translate-y-1" />
</div>
<p className="mt-4 max-w-3xl text-base leading-7 text-white/82 sm:text-lg">
{article.summary}
</p>
</div>
<div className="grid gap-6 lg:grid-cols-[1fr_auto] lg:items-end">
<div className="flex flex-wrap gap-8">
<MetaBlock label="Written by">
<div className="flex items-center gap-2">
<AvatarChip article={article} />
<span>{article.author.name}</span>
</div>
</MetaBlock>
<MetaBlock label="Published on">
<span className="inline-flex h-10 items-center">{article.publishedAt}</span>
</MetaBlock>
</div>
<MetaBlock label="Filed under">
<div className="flex h-10 items-center gap-2">
{article.tags.map((tag) => (
<span key={tag.name} className="rounded-full px-2 py-0.5 text-xs font-medium ring-1 ring-white/90">
{tag.name}
</span>
))}
</div>
</MetaBlock>
</div>
</div>
</div>
</a>
);
}
function ArticleCard({ article }: { article: Article }) {
return (
<a href="#" className="group block outline-none">
<div
className="aspect-[1.45/1] overflow-hidden rounded-2xl border border-zinc-200 bg-cover bg-center shadow-lg shadow-zinc-950/5 transition group-hover:shadow-xl group-hover:shadow-zinc-950/10 dark:border-white/10"
style={{ backgroundImage: `url('${article.thumbnailUrl}')` }}
role="img"
aria-label={article.title}
>
<div className="h-full w-full bg-gradient-to-t from-zinc-950/18 via-transparent to-white/8 transition group-hover:bg-zinc-950/5" />
</div>
<div className="mt-5">
<p className="text-sm font-semibold text-violet-600 dark:text-violet-300">
{article.category}
</p>
<div className="mt-2 flex gap-3">
<h3 className="flex-1 text-xl font-semibold tracking-tight text-zinc-950 transition group-hover:text-violet-700 dark:text-white dark:group-hover:text-violet-200">
{article.title}
</h3>
<ArrowUpRight className="mt-1 size-5 shrink-0 text-zinc-500 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-zinc-950 dark:group-hover:text-white" />
</div>
<p className="mt-2 line-clamp-2 text-sm leading-6 text-zinc-600 dark:text-zinc-400">
{article.summary}
</p>
<div className="mt-5 flex items-center gap-3">
<AvatarChip article={article} dark />
<div>
<p className="text-sm font-semibold text-zinc-950 dark:text-white">{article.author.name}</p>
<p className="text-sm text-zinc-500 dark:text-zinc-400">
{article.publishedAt} · {article.readingTime}
</p>
</div>
</div>
<div className="mt-5 flex flex-wrap gap-2">
{article.tags.map((tag) => (
<span
key={tag.name}
className={cn(
"rounded-full px-2.5 py-1 text-xs font-medium",
tagStyles[tag.tone],
)}
>
{tag.name}
</span>
))}
</div>
</div>
</a>
);
}
function AvatarChip({ article, dark = false }: { article: Article; dark?: boolean }) {
return (
<span
className={cn(
"grid size-10 shrink-0 place-items-center overflow-hidden rounded-full border bg-cover bg-center text-xs font-bold",
dark ? "border-zinc-200 dark:border-white/10" : "border-white/40",
)}
style={{ backgroundImage: `url('${article.author.avatarUrl}')` }}
aria-label={article.author.name}
>
<span className="sr-only">{article.author.initials}</span>
</span>
);
}
function MetaBlock({ label, children }: { label: string; children: React.ReactNode }) {
return (
<div className="space-y-2 text-sm font-semibold text-white">
<p>{label}</p>
<div>{children}</div>
</div>
);
}
function Pagination() {
return (
<nav className="mt-14 flex items-center justify-between border-t border-zinc-200 pt-5 text-sm font-semibold text-zinc-700 dark:border-white/10 dark:text-zinc-300">
<a href="#" className="inline-flex items-center gap-2 transition hover:text-zinc-950 dark:hover:text-white">
<ArrowLeft className="size-4" />
Previous
</a>
<div className="hidden items-center gap-1 sm:flex">
{[1, 2, 3].map((page) => (
<a
key={page}
href="#"
className={cn(
"grid size-9 place-items-center rounded-full transition",
page === 1
? "bg-zinc-950 text-white dark:bg-white dark:text-zinc-950"
: "hover:bg-zinc-100 dark:hover:bg-white/10",
)}
>
{page}
</a>
))}
<span className="px-2 text-zinc-400">...</span>
<a href="#" className="grid size-9 place-items-center rounded-full transition hover:bg-zinc-100 dark:hover:bg-white/10">
8
</a>
</div>
<a href="#" className="inline-flex items-center gap-2 transition hover:text-zinc-950 dark:hover:text-white">
Next
<ArrowRight className="size-4" />
</a>
</nav>
);
}
const tagStyles: Record<ArticleTag["tone"], string> = {
violet: "bg-violet-50 text-violet-700 ring-1 ring-violet-200 dark:bg-violet-400/10 dark:text-violet-200 dark:ring-violet-400/20",
blue: "bg-sky-50 text-sky-700 ring-1 ring-sky-200 dark:bg-sky-400/10 dark:text-sky-200 dark:ring-sky-400/20",
emerald: "bg-emerald-50 text-emerald-700 ring-1 ring-emerald-200 dark:bg-emerald-400/10 dark:text-emerald-200 dark:ring-emerald-400/20",
rose: "bg-rose-50 text-rose-700 ring-1 ring-rose-200 dark:bg-rose-400/10 dark:text-rose-200 dark:ring-rose-400/20",
amber: "bg-amber-50 text-amber-700 ring-1 ring-amber-200 dark:bg-amber-400/10 dark:text-amber-200 dark:ring-amber-400/20",
slate: "bg-zinc-100 text-zinc-700 ring-1 ring-zinc-200 dark:bg-white/10 dark:text-zinc-200 dark:ring-white/10",
};
export default BlogHeaderFeaturedPosts;
next-ui/blocks/blog-content-sections/blog-hover-image-card.tsx
"use client";
import * as React from "react";
import { ArrowUpRight } from "lucide-react";
import { motion, useReducedMotion } from "framer-motion";
import { cn } from "@/lib/utils";
export type BlogHoverImageCardProps = {
className?: string;
author?: string;
title?: string;
tag?: string;
date?: string;
mainImage?: string;
hoverImage?: string;
tagClassName?: string;
arrowClassName?: string;
};
const defaultMainImage =
"https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=1200&q=80";
const defaultHoverImage =
"https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=500&q=80";
export function BlogHoverImageCard({
className,
author = "Noah Williams",
title = "Designing launch pages with reusable UI systems",
tag = "Design Systems",
date = "23 May '26",
mainImage = defaultMainImage,
hoverImage = defaultHoverImage,
tagClassName,
arrowClassName,
}: BlogHoverImageCardProps) {
const shouldReduceMotion = useReducedMotion();
return (
<motion.article
initial="rest"
whileHover="hover"
animate="rest"
className={cn(
"group relative flex h-[425px] w-full max-w-[300px] cursor-pointer flex-col gap-4 overflow-hidden rounded-[14px] bg-white text-zinc-950",
className,
)}
>
<div
className="relative flex-1 overflow-hidden rounded-xl bg-cover bg-center"
style={{ backgroundImage: `url('${mainImage}')` }}
role="img"
aria-label={title}
>
<motion.div
variants={{
rest: { scale: 1 },
hover: { scale: shouldReduceMotion ? 1 : 1.04 },
}}
transition={{ duration: 0.45, ease: [0.35, 0.13, 0, 0.99] }}
className="absolute inset-0 bg-gradient-to-t from-zinc-950/16 via-transparent to-white/10"
/>
<motion.div
variants={{
rest: { y: 112 },
hover: { y: 0 },
}}
transition={{ duration: shouldReduceMotion ? 0 : 0.4, ease: [0.35, 0.13, 0, 0.99] }}
className="absolute bottom-0 left-0 flex h-28 w-[92px] flex-col overflow-hidden"
>
<Corner className="left-0 top-0 rotate-0" />
<div className="flex h-[100px] w-full">
<div className="flex h-[100px] w-20 items-center justify-center rounded-bl-xl rounded-tr-xl bg-white">
<div
className="h-20 w-[60px] rounded-md bg-cover bg-center"
style={{ backgroundImage: `url('${hoverImage}')` }}
role="img"
aria-label={`${author} portrait`}
/>
</div>
<div className="relative h-[100px] w-3 overflow-hidden">
<Corner className="left-0 top-[88px] rotate-0" />
</div>
</div>
</motion.div>
</div>
<div className="space-y-4">
<div className="space-y-4">
<p className="text-sm text-zinc-400">{author}</p>
<h3 className="line-clamp-2 text-lg font-medium leading-snug text-zinc-700">
{title}
</h3>
</div>
<div className="relative flex items-center justify-between gap-3">
<span
className={cn(
"max-w-[150px] truncate rounded-full bg-zinc-200 px-3 py-1.5 text-sm text-zinc-600",
tagClassName,
)}
>
{tag}
</span>
<span className="min-w-0 flex-1 truncate px-1.5 py-1.5 text-sm text-zinc-900">
{date}
</span>
<motion.span
variants={{
rest: { x: 18 },
hover: { x: 0 },
}}
transition={{ duration: shouldReduceMotion ? 0 : 0.4, ease: [0.35, 0.13, 0, 0.99] }}
className={cn(
"absolute right-0 top-1/2 grid size-4 -translate-y-1/2 place-items-center rounded bg-zinc-950 text-white",
arrowClassName,
)}
>
<ArrowUpRight className="size-3" strokeWidth={1.8} />
</motion.span>
</div>
</div>
</motion.article>
);
}
export function BlogHoverImageCardShowcase({ className }: { className?: string }) {
return (
<section
className={cn(
"flex min-h-full w-full items-center justify-center overflow-y-auto bg-white p-6 text-zinc-950 dark:bg-zinc-950",
className,
)}
>
<div className="grid w-full max-w-5xl gap-6 sm:grid-cols-2 lg:grid-cols-3">
<BlogHoverImageCard
author="Noah Williams"
title="Designing launch pages with reusable UI systems"
tag="Design Systems"
date="23 May '26"
mainImage="https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=1200&q=80"
hoverImage="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=500&q=80"
/>
<BlogHoverImageCard
author="Ariana Reed"
title="How animated cards create a stronger editorial rhythm"
tag="Motion"
date="18 May '26"
mainImage="https://images.unsplash.com/photo-1518005020951-eccb494ad742?auto=format&fit=crop&w=1200&q=80"
hoverImage="https://images.unsplash.com/photo-1531123897727-8f129e1688ce?auto=format&fit=crop&w=500&q=80"
tagClassName="bg-violet-100 text-violet-700"
arrowClassName="bg-violet-600"
/>
<BlogHoverImageCard
author="Theo Banerjee"
title="A practical guide to product stories that feel premium"
tag="Launch"
date="14 May '26"
mainImage="https://images.unsplash.com/photo-1553877522-43269d4ea984?auto=format&fit=crop&w=1200&q=80"
hoverImage="https://images.unsplash.com/photo-1527980965255-d3b416303d12?auto=format&fit=crop&w=500&q=80"
tagClassName="bg-sky-100 text-sky-700"
arrowClassName="bg-sky-600"
/>
</div>
</section>
);
}
function Corner({ className }: { className?: string }) {
return (
<svg
viewBox="0 0 12 12"
className={cn("absolute size-3 text-white", className)}
aria-hidden="true"
>
<path d="M0 12H12C5.373 12 0 6.627 0 0Z" fill="currentColor" />
</svg>
);
}
export default BlogHoverImageCard;
next-ui/blocks/blog-content-sections/index.tsx
"use client";
import { FileText, ImageIcon, ListTree, Newspaper, PanelsTopLeft } from "lucide-react";
import { cn } from "@/lib/utils";
import { BlogContentCentered } from "./blog-content-centered";
import { BlogContentEditorial } from "./blog-content-editorial";
import { BlogHeaderFeaturedPosts } from "./blog-header-featured-posts";
import { BlogHoverImageCardShowcase } from "./blog-hover-image-card";
import { BlogContentWithToc } from "./blog-content-with-toc";
export { BlogContentCentered } from "./blog-content-centered";
export type { BlogContentCenteredProps } from "./blog-content-centered";
export { BlogContentEditorial } from "./blog-content-editorial";
export type { BlogContentEditorialProps } from "./blog-content-editorial";
export { BlogHeaderFeaturedPosts } from "./blog-header-featured-posts";
export type { BlogHeaderFeaturedPostsProps } from "./blog-header-featured-posts";
export { BlogHoverImageCard, BlogHoverImageCardShowcase } from "./blog-hover-image-card";
export type { BlogHoverImageCardProps } from "./blog-hover-image-card";
export { BlogContentWithToc } from "./blog-content-with-toc";
export type { BlogContentWithTocProps } from "./blog-content-with-toc";
const variants = [
{
title: "Blog Content Centered",
description: "A narrow editorial article layout with large images and readable prose.",
icon: FileText,
},
{
title: "Blog Content With TOC",
description: "Sticky section navigation for long single-post pages.",
icon: ListTree,
},
{
title: "Blog Content Editorial",
description: "A premium split story layout for essays and launch notes.",
icon: Newspaper,
},
{
title: "Blog Header Featured Posts",
description: "Featured hero story, category tabs, article grid, and pagination.",
icon: PanelsTopLeft,
},
{
title: "Blog Hover Image Card",
description: "A compact editorial card with a sliding author image and arrow hover state.",
icon: ImageIcon,
},
];
export function BlogContentSectionsBlock({ className }: { className?: string }) {
return (
<section
className={cn(
"min-h-full w-full overflow-y-auto bg-zinc-950 p-4 text-white sm:p-6 lg:p-8",
className,
)}
>
<div className="mx-auto max-w-7xl">
<div className="rounded-[2rem] border border-white/10 bg-white/[0.04] p-5 shadow-2xl shadow-black/30 sm:p-6 lg:p-8">
<div className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<div>
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-violet-200/70">
Blog Content Sections
</p>
<h2 className="mt-3 max-w-2xl text-3xl font-semibold tracking-tight text-white sm:text-4xl">
Content sections for your single blog posts.
</h2>
</div>
<p className="max-w-md text-sm leading-6 text-white/50">
Drop in centered articles, table-of-contents posts, or editorial
feature stories with polished spacing and responsive typography.
</p>
</div>
<div className="mt-7 grid gap-4 md:grid-cols-2 xl:grid-cols-5">
{variants.map((item) => {
const Icon = item.icon;
return (
<div key={item.title} className="rounded-2xl border border-white/10 bg-white/[0.055] p-4">
<div className="grid size-10 place-items-center rounded-xl bg-white text-zinc-950">
<Icon className="size-4" />
</div>
<h3 className="mt-4 text-lg font-semibold">{item.title}</h3>
<p className="mt-2 text-sm leading-6 text-white/54">{item.description}</p>
</div>
);
})}
</div>
<div className="mt-6 grid gap-5 lg:grid-cols-2 xl:grid-cols-5">
<div className="overflow-hidden rounded-[1.5rem] border border-white/10 bg-white">
<div className="h-[520px] overflow-hidden">
<BlogContentCentered className="h-full min-h-full" />
</div>
</div>
<div className="overflow-hidden rounded-[1.5rem] border border-white/10 bg-white">
<div className="h-[520px] overflow-hidden">
<BlogContentWithToc className="h-full min-h-full" />
</div>
</div>
<div className="overflow-hidden rounded-[1.5rem] border border-white/10 bg-zinc-950">
<div className="h-[520px] overflow-hidden">
<BlogContentEditorial className="h-full min-h-full" />
</div>
</div>
<div className="overflow-hidden rounded-[1.5rem] border border-white/10 bg-white">
<div className="h-[520px] overflow-hidden">
<BlogHeaderFeaturedPosts className="h-full min-h-full" />
</div>
</div>
<div className="overflow-hidden rounded-[1.5rem] border border-white/10 bg-white">
<div className="h-[520px] overflow-hidden">
<BlogHoverImageCardShowcase className="h-full min-h-full" />
</div>
</div>
</div>
</div>
</div>
</section>
);
}
export function BlogContentSectionsBlockPreview() {
return (
<div className="relative h-[320px] w-full overflow-hidden rounded-3xl bg-[#f7f7f7] dark:bg-zinc-950">
<div className="absolute left-1/2 top-0 w-[760px] -translate-x-1/2 scale-[0.52] origin-top">
<BlogContentCentered className="h-[620px] min-h-0 overflow-hidden rounded-3xl py-10" />
</div>
</div>
);
}
export default BlogContentSectionsBlock;