shadimage
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 · medium
animationframer-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;

More from nexus-labs

Browse all components in this library →