Blog

Blog layouts for featured stories, cards, and reading lists.

A small set of blog layouts, from quick scans to denser indexes. Bring your own stories, covers, and authors.

Post cards

Three straightforward cards with a cover, category, title, and excerpt.

From the blog

Notes on product, design, and shipping interfaces that feel finished.

  • ProductMar 12, 2026

    Shipping blocks that match your tokens

    How we keep every section restylable without a second design pass.

  • EngineeringMar 4, 2026

    A boring install story on purpose

    CLI, drop-in, ship — why the path to first paint should feel uneventful.

  • DesignFeb 21, 2026

    Light, dark, and everything between

    Semantic surfaces so marketing pages don’t need a night-mode rewrite.

tsx
"use client";import { cn } from "@/lib/utils";type Blog01Props = {  className?: string;};const POSTS = [  {    title: "Shipping blocks that match your tokens",    excerpt: "How we keep every section restylable without a second design pass.",    category: "Product",    date: "Mar 12, 2026",    image: "/assets/brand/demos/attachments/attachment-1.png",  },  {    title: "A boring install story on purpose",    excerpt: "CLI, drop-in, ship — why the path to first paint should feel uneventful.",    category: "Engineering",    date: "Mar 4, 2026",    image: "/assets/brand/demos/attachments/attachment-2.png",  },  {    title: "Light, dark, and everything between",    excerpt: "Semantic surfaces so marketing pages don’t need a night-mode rewrite.",    category: "Design",    date: "Feb 21, 2026",    image: "/assets/brand/demos/attachments/attachment-3.png",  },] as const;export function Blog01({ className }: Blog01Props) {  return (    <section className={cn("@container w-full bg-background", className)}>      <div className="mx-auto w-full max-w-4xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14">        <div className="mx-auto max-w-md text-center">          <h2 className="text-xl font-medium tracking-wide text-foreground @[32rem]:text-2xl">            From the blog          </h2>          <p className="mt-2 text-sm tracking-wide text-muted-foreground">            Notes on product, design, and shipping interfaces that feel finished.          </p>        </div>        <ul className="mt-8 grid grid-cols-1 gap-6 @[40rem]:grid-cols-3 @[40rem]:gap-4">          {POSTS.map((post) => (            <li key={post.title} className="flex min-w-0 flex-col">              <div className="aspect-[16/10] w-full overflow-hidden rounded-xl bg-muted">                <img                  src={post.image}                  alt=""                  className="size-full object-cover"                />              </div>              <div className="mt-3 flex items-center justify-between gap-3 text-[11px] tracking-wide text-muted-foreground">                <span>{post.category}</span>                <span>{post.date}</span>              </div>              <h3 className="mt-1.5 line-clamp-2 text-sm font-medium tracking-wide text-foreground">                {post.title}              </h3>              <p className="mt-1.5 line-clamp-2 text-xs tracking-wide text-muted-foreground">                {post.excerpt}              </p>            </li>          ))}        </ul>      </div>    </section>  );}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add blog-01

Usage

import { Blog01 } from "@/components/blocks/blog/blog-01"
<Blog01 />

One full-width feature followed by three smaller posts in a row.

Latest writing

A featured story up top, then three recent posts underneath.

Product·Mar 18, 2026

Shipping a full marketing kit

Patterns that held up when teams started installing blocks.

  • DesignMar 10, 2026

    Token-ready defaults

    Restyle every section without a second design pass.

  • EngineeringMar 2, 2026

    Keep the CLI boring

    Install, import, ship — no ceremony required.

  • GuidesFeb 24, 2026

    Compose without fights

    Mix heroes, pricing, and FAQs in any order.

tsx
"use client";import { cn } from "@/lib/utils";type Blog02Props = {  className?: string;};const FEATURED = {  title: "Shipping a full marketing kit",  excerpt: "Patterns that held up when teams started installing blocks.",  category: "Product",  date: "Mar 18, 2026",  image: "/assets/brand/demos/attachments/attachment-4.png",} as const;const POSTS = [  {    title: "Token-ready defaults",    excerpt: "Restyle every section without a second design pass.",    category: "Design",    date: "Mar 10, 2026",    image: "/assets/brand/demos/attachments/attachment-1.png",  },  {    title: "Keep the CLI boring",    excerpt: "Install, import, ship — no ceremony required.",    category: "Engineering",    date: "Mar 2, 2026",    image: "/assets/brand/demos/attachments/attachment-2.png",  },  {    title: "Compose without fights",    excerpt: "Mix heroes, pricing, and FAQs in any order.",    category: "Guides",    date: "Feb 24, 2026",    image: "/assets/brand/demos/attachments/attachment-3.png",  },] as const;export function Blog02({ className }: Blog02Props) {  return (    <section className={cn("@container w-full bg-background", className)}>      <div className="mx-auto w-full max-w-4xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14">        <div className="mx-auto max-w-md text-center">          <h2 className="text-xl font-medium tracking-wide text-foreground @[32rem]:text-2xl">            Latest writing          </h2>          <p className="mt-2 text-sm tracking-wide text-muted-foreground">            A featured story up top, then three recent posts underneath.          </p>        </div>        <article className="relative mt-8 aspect-[21/9] overflow-hidden rounded-xl bg-muted @[32rem]:aspect-[2.4/1]">          <img            src={FEATURED.image}            alt=""            className="absolute inset-0 size-full object-cover"          />          <div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent" />          <div className="absolute inset-x-0 bottom-0 p-4 @[32rem]:p-6">            <p className="text-[11px] tracking-wide text-white/75">              {FEATURED.category}              <span className="mx-1.5 text-white/40">·</span>              {FEATURED.date}            </p>            <h3 className="mt-1.5 line-clamp-2 text-sm font-medium tracking-wide text-white @[32rem]:text-base">              {FEATURED.title}            </h3>            <p className="mt-1.5 line-clamp-2 max-w-xl text-xs tracking-wide text-white/75">              {FEATURED.excerpt}            </p>          </div>        </article>        <ul className="mt-8 grid grid-cols-1 gap-6 @[40rem]:grid-cols-3 @[40rem]:gap-4">          {POSTS.map((post) => (            <li key={post.title} className="flex min-w-0 flex-col">              <div className="aspect-[16/10] w-full overflow-hidden rounded-xl bg-muted">                <img                  src={post.image}                  alt=""                  className="size-full object-cover"                />              </div>              <div className="mt-3 flex items-center justify-between gap-3 text-[11px] tracking-wide text-muted-foreground">                <span>{post.category}</span>                <span>{post.date}</span>              </div>              <h3 className="mt-1.5 line-clamp-2 text-sm font-medium tracking-wide text-foreground">                {post.title}              </h3>              <p className="mt-1.5 line-clamp-2 text-xs tracking-wide text-muted-foreground">                {post.excerpt}              </p>            </li>          ))}        </ul>      </div>    </section>  );}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add blog-02

Usage

import { Blog02 } from "@/components/blocks/blog/blog-02"
<Blog02 />

Reading list

Compact rows with covers, excerpts, and authors for a denser reading index.

All posts

A simple reading list with author and cover on each row.

  • ProductMar 12, 2026

    Shipping blocks that match your tokens

    Keep every section restylable without a second design pass.

    Maya Chen
  • EngineeringMar 4, 2026

    A boring install story on purpose

    Why the path to first paint should feel uneventful.

    Priya Nair
  • DesignFeb 21, 2026

    Light, dark, and everything between

    Semantic surfaces so night mode isn’t a rewrite.

    Jordan Hale
  • GuidesFeb 14, 2026

    Composing pages without layout fights

    Mix heroes, pricing, and FAQs without nesting wars.

    Chris Ortega
tsx
"use client";import { cn } from "@/lib/utils";type Blog03Props = {  className?: string;};const POSTS = [  {    title: "Shipping blocks that match your tokens",    excerpt: "Keep every section restylable without a second design pass.",    category: "Product",    date: "Mar 12, 2026",    author: "Maya Chen",    avatar: "/assets/brand/demos/avatars/avatar-1.png",    image: "/assets/brand/demos/attachments/attachment-1.png",  },  {    title: "A boring install story on purpose",    excerpt: "Why the path to first paint should feel uneventful.",    category: "Engineering",    date: "Mar 4, 2026",    author: "Priya Nair",    avatar: "/assets/brand/demos/avatars/avatar-3.png",    image: "/assets/brand/demos/attachments/attachment-2.png",  },  {    title: "Light, dark, and everything between",    excerpt: "Semantic surfaces so night mode isn’t a rewrite.",    category: "Design",    date: "Feb 21, 2026",    author: "Jordan Hale",    avatar: "/assets/brand/demos/avatars/avatar-2.png",    image: "/assets/brand/demos/attachments/attachment-3.png",  },  {    title: "Composing pages without layout fights",    excerpt: "Mix heroes, pricing, and FAQs without nesting wars.",    category: "Guides",    date: "Feb 14, 2026",    author: "Chris Ortega",    avatar: "/assets/brand/demos/avatars/avatar-4.png",    image: "/assets/brand/demos/attachments/attachment-5.png",  },] as const;export function Blog03({ className }: Blog03Props) {  return (    <section className={cn("@container w-full bg-background", className)}>      <div className="mx-auto w-full max-w-3xl px-4 py-10 @[32rem]:px-6 @[32rem]:py-14">        <div className="mx-auto max-w-md text-center">          <h2 className="text-xl font-medium tracking-wide text-foreground @[32rem]:text-2xl">            All posts          </h2>          <p className="mt-2 text-sm tracking-wide text-muted-foreground">            A simple reading list with author and cover on each row.          </p>        </div>        <ul className="mt-8 flex flex-col gap-3">          {POSTS.map((post) => (            <li              key={post.title}              className="flex gap-4 rounded-xl bg-muted p-3 @[32rem]:p-4"            >              <div className="hidden size-24 shrink-0 overflow-hidden rounded-lg bg-background @[32rem]:block">                <img                  src={post.image}                  alt=""                  className="size-full object-cover"                />              </div>              <div className="flex min-w-0 flex-1 flex-col justify-center">                <div className="flex items-center justify-between gap-3 text-[11px] tracking-wide text-muted-foreground">                  <span>{post.category}</span>                  <span>{post.date}</span>                </div>                <h3 className="mt-1 line-clamp-2 text-sm font-medium tracking-wide text-foreground">                  {post.title}                </h3>                <p className="mt-1 line-clamp-2 text-xs tracking-wide text-muted-foreground">                  {post.excerpt}                </p>                <div className="mt-3 flex items-center gap-2">                  <img                    src={post.avatar}                    alt=""                    className="size-5 rounded-full object-cover"                  />                  <span className="text-[11px] tracking-wide text-muted-foreground">                    {post.author}                  </span>                </div>              </div>            </li>          ))}        </ul>      </div>    </section>  );}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add blog-03

Usage

import { Blog03 } from "@/components/blocks/blog/blog-03"
<Blog03 />