Article Sidebar
A complete article sidebar section, ready to adapt to your product.
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.jsonUse 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";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;
ArticleSidebarPropsSource
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 →