Skip to content
Blocks

Article Sidebar

A complete article sidebar section, ready to adapt to your product.

Open ↗

Make it yours.

Install the editable source and its dependencies into your React + Tailwind project.

Scroll horizontally for long lines
pnpm dlx shadcn@4.0.8 add http://localhost:3000/r/article-sidebar.json

Use it

Scroll horizontally for long lines
"use client";
import { ArticleSidebar } from "@/components/jez-ui/blocks/article-sidebar";

export default function Example() {
  return (
    <>
      <ArticleSidebar />
    </>
  );
}

Compose your own

These styled parts are included in the same install. Use children to build your layout; add className only when you want an override.

Scroll horizontally for long lines
import { ArticleSidebar, ArticleSidebarTitle, ArticleSidebarContent, ArticleSidebarItemTitle } from "@/components/jez-ui/blocks/article-sidebar";
Read the composition guide →

Props & types

Scroll horizontally for long lines
export type ArticleSidebarOptions = {
  className?: string;
  title?: string;
  children?: React.ReactNode;
  aside?: React.ReactNode;
  imageSrc?: string;
  imageAlt?: string;
};

export type ArticleSidebarProps = Omit<
  React.ComponentProps<"article">,
  keyof ArticleSidebarOptions
> &
  ArticleSidebarOptions;

ArticleSidebarProps

Source

View 3 source files

registry/blocks/article-sidebar.tsx

Scroll horizontally for long lines
"use client";
import * as React from "react";
import { cn } from "../ui/utils";
export type ArticleSidebarOptions = {
  className?: string;
  title?: string;
  children?: React.ReactNode;
  aside?: React.ReactNode;
  imageSrc?: string;
  imageAlt?: string;
};
export type ArticleSidebarProps = Omit<
  React.ComponentProps<"article">,
  keyof ArticleSidebarOptions
> &
  ArticleSidebarOptions;
export function ArticleSidebar({
  className,
  title = "Make space for the work.",
  children,
  aside,
  imageSrc = "/assets/editorial-question.svg",
  imageAlt = "An editorial study of questions",
  ...rootProps
}: ArticleSidebarProps) {
  return (
    <article {...rootProps} className={cn("py-8", className)}>
      <ArticleSidebarTitle>{title}</ArticleSidebarTitle>
      <ArticleSidebarContent>
        <div className="min-w-0 space-y-6 text-base leading-relaxed">
          {children ?? (
            <>
              <p>
                Every project begins with a question. The useful ones rarely
                arrive fully formed. They emerge when we leave enough room to
                notice what is already there.
              </p>
              <figure>
                <img
                  src={imageSrc}
                  alt={imageAlt}
                  className="aspect-[3/2] w-full rounded-xl object-cover"
                />
                <figcaption className="mt-3 text-xs text-muted-foreground">
                  From the studio notebook. Illustrative editorial content.
                </figcaption>
              </figure>
              <ArticleSidebarItemTitle>
                Start with what you notice
              </ArticleSidebarItemTitle>
              <p>
                A small detail can change the direction of an entire piece of
                work. Collect observations before solutions. Give the unfamiliar
                idea another day.
              </p>
            </>
          )}
        </div>
        <aside className="border-t border-border pt-6 md:border-l md:border-t-0 md:pl-6 md:pt-0">
          {aside ?? (
            <>
              <h3 className="text-lg">In the margins</h3>
              <p className="mt-4 text-sm leading-relaxed text-muted-foreground">
                A collection of notes on attention, creative practice, and
                making things with care.
              </p>
              <a
                href="/templates/editorial"
                className="mt-6 inline-block text-sm underline underline-offset-4"
              >
                Explore the journal
              </a>
            </>
          )}
        </aside>
      </ArticleSidebarContent>
    </article>
  );
}

export function ArticleSidebarTitle({
  className,
  ...props
}: React.ComponentProps<"h2">) {
  return (
    <h2
      data-slot="article-sidebar-title"
      className={cn(
        "max-w-2xl text-4xl leading-tight tracking-tight md:text-6xl",
        className,
      )}
      {...props}
    />
  );
}
export function ArticleSidebarContent({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="article-sidebar-content"
      className={cn(
        "mt-10 grid gap-10 md:grid-cols-[minmax(0,1fr)_220px]",
        className,
      )}
      {...props}
    />
  );
}
export function ArticleSidebarItemTitle({
  className,
  ...props
}: React.ComponentProps<"h3">) {
  return (
    <h3
      data-slot="article-sidebar-itemtitle"
      className={cn("text-2xl", className)}
      {...props}
    />
  );
}

registry/ui/utils.ts

Scroll horizontally for long lines
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...values: ClassValue[]) {
  return twMerge(clsx(values));
}

assets/editorial-question.svg

Scroll horizontally for long lines
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 900"><rect width="900" height="900" fill="#b6c6d2"/><g fill="none" stroke="#243d55" stroke-width="3"><circle cx="450" cy="430" r="285"/><circle cx="450" cy="430" r="250"/><circle cx="450" cy="430" r="215"/><circle cx="450" cy="430" r="180"/><circle cx="450" cy="430" r="145"/><path d="M0 430H900M450 0V900" stroke-width="1"/></g><path d="M450 110a320 320 0 01277 480L450 430z" fill="#eee9dd"/><circle cx="450" cy="430" r="98" fill="#b85e35"/><circle cx="450" cy="430" r="25" fill="#eee9dd"/><path d="M660 95v125h125" fill="none" stroke="#243d55" stroke-width="5"/></svg>

Dependencies

clsx@2.1.1 · tailwind-merge@3.5.0

Accessibility & behaviour

Provide meaningful labels and preserve keyboard focus styles. Check contrast when customising theme colours.

Read the accessibility and performance guide →