Customer story pageDesign system
Site

Customer story page

Customer story layout with a back link, title, date, and article column.

npx context-ds add customer-story-page
On the site

Usage

Use it for

The page for one customer story (or a case study), followed by the closing call to action. Put the story's prose (the blog's article body) in as children; the dot field goes in backdrop.

Avoid

  • A centered title
  • A dark hero band on a story page
  • Cover images above the title

Dependencies: section

Example

examples/customer-story-page.tsx
import { CustomerStoryPage } from "@/components/site/customer-story-page";export function Site() {  return (    <CustomerStoryPage title="Mastra personalizes onboarding" accent="with Context.dev" date="September 12, 2026" dateTime="2026-09-12">      <div className="flex flex-col gap-5 text-body-lg leading-relaxed text-fg-secondary">        <p>Mastra built an onboarding MVP in an hour, pre-filling names and company details from a work email and tailoring the first screen to each user's goals.</p>        <p>Before, new users typed the same details into four fields. Now the form fills itself, and the team ships changes to it in an afternoon.</p>      </div>    </CustomerStoryPage>  );}

Source

components/site/customer-story-page.tsx
import * as React from "react";import { SectionHeaderRule, SiteSection } from "@/components/site/section";import { cn } from "@/lib/utils";// A customer story's page: the first section under the header, its column edge lines starting below the navbar. A back// link to all the stories, the title at section-heading size with its closing words in blue, and the date, left aligned// in a 48rem column over an optional backdrop (the site runs its fine dot field here, from edge line to edge line and// down to the rule). Under the rule, the story itself in the same column (the blog's prose).function CustomerStoryPage({  title,  accent,  date,  dateTime,  backHref = "/customers",  backLabel = "All customer stories",  backdrop,  className,  children,}: {  title: React.ReactNode;  /** The title's closing words, in brand blue. */  accent?: React.ReactNode;  date?: string;  dateTime?: string;  backHref?: string;  backLabel?: string;  backdrop?: React.ReactNode;  className?: string;  children: React.ReactNode;}) {  const titleId = React.useId();  return (    <SiteSection lines header="below" aria-labelledby={titleId} data-slot="customer-story-page" className={className}>      {/* The section's padding is taken back, so the backdrop runs from edge line to edge line and down to the rule. */}      <div className="relative isolate -mx-6 -mt-10 -mb-10 px-6 pt-10 pb-10 md:-mx-10 md:-mt-12 md:-mb-12 md:px-10 md:pt-12 md:pb-12 lg:-mx-12 lg:px-12">        {backdrop && (          <div aria-hidden="true" className="pointer-events-none absolute inset-0 -z-10">            {backdrop}          </div>        )}        <header className="relative mx-auto flex max-w-3xl flex-col items-start gap-6">          <a href={backHref} className="rounded-sm text-body-sm leading-snug text-fg-nav outline-none transition-[color] duration-150 ease-out hover:text-fg focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus-ring motion-reduce:transition-none">            &larr; {backLabel}          </a>          <h1 id={titleId} className="w-full text-h3 leading-heading font-normal tracking-normal text-balance text-fg md:text-h2">            {title}            {accent && (              <>                {" "}                <span className="text-fg-brand">{accent}</span>              </>            )}          </h1>          {date && (            <time dateTime={dateTime} className="text-body-sm leading-snug text-fg-nav">              {date}            </time>          )}        </header>      </div>      <SectionHeaderRule />      <div className={cn("mx-auto max-w-3xl")}>{children}</div>    </SiteSection>  );}export { CustomerStoryPage };

Reference: src/app/(marketing)/customers/[slug]/page.tsx.