Case Study Story
Give the work a complete story: a cinematic image, useful project facts, readable chapters, clear outcomes, and the next project.
A home, in its element.
A house that asks less of its surroundings. Open to the light, grounded in its materials, and made for the small rituals of everyday life.
- Project
- Hearth House
- Location
- Coastal New South Wales
- Scope
- Architecture & interiors
- Status
- Residential concept / 2026

Less building. More belonging.
The brief began with a feeling, rather than a floor plan: a place to slow down, where the landscape remains the main event. The challenge was to create privacy and shelter without closing the house off from the world around it.
A low roofline, a simple footprint, and a generous connection to the garden gave the concept its starting point. Every room looks outward. Every threshold makes space to pause.
Let the site do the talking.
We brought the living spaces to the light and tucked the quieter rooms behind a sheltered edge. Deep eaves temper the afternoon sun; opposing openings invite a breeze through the plan.
The palette stays deliberately small: tactile timber, soft mineral surfaces, and planting allowed to grow into the edges. Fewer gestures, considered more carefully.

A quieter kind of impact.
Illustrative design-study specifications. This fictional project demonstrates the component; it is not a completed building or a performance claim.
- m² considered footprint
- 186
- core material families
- 03
- clear connection to place
- 01

Headland Retreat
'use client'
import { useId } from 'react'
import './case-study-story.css'
export interface CaseStudyImage {
src: string
alt: string
caption?: string
}
export interface CaseStudyFact {
label: string
value: string
}
export interface CaseStudyChapter {
id: string
title: string
paragraphs: string[]
image?: CaseStudyImage
}
export interface CaseStudyOutcome {
value: string
label: string
}
export interface CaseStudyNextProject {
title: string
href: string
label?: string
image?: CaseStudyImage
}
export interface CaseStudyStoryProps {
title: string
intro: string
hero: CaseStudyImage
chapters: CaseStudyChapter[]
eyebrow?: string
facts?: CaseStudyFact[]
outcomes?: CaseStudyOutcome[]
outcomesTitle?: string
outcomeNote?: string
nextProject?: CaseStudyNextProject
showChapterLinks?: boolean
className?: string
}
function StoryImage({ image, className = '' }: { image: CaseStudyImage; className?: string }) {
return <figure className={`ac-case-image ${className}`.trim()}><img src={image.src} alt={image.alt} loading="lazy" />{image.caption && <figcaption>{image.caption}</figcaption>}</figure>
}
/** An editorial project story with readable chapters, project facts, and outcomes. */
export function CaseStudyStory({ title, intro, hero, chapters, eyebrow = 'SELECTED WORK', facts = [], outcomes = [], outcomesTitle = 'The outcome.', outcomeNote, nextProject, showChapterLinks = true, className = '' }: CaseStudyStoryProps) {
const id = useId()
return <article className={`ac-case-study ${className}`.trim()} aria-labelledby={`${id}-title`}>
<header className="ac-case-header">
<div className="ac-case-topline"><span>{eyebrow}</span><span aria-hidden="true">A CLOSER LOOK / ↘</span></div>
<h2 id={`${id}-title`}>{title}</h2>
<div className="ac-case-intro"><p>{intro}</p>{facts.length > 0 && <dl className="ac-case-facts">{facts.map((fact, index) => <div key={`${fact.label}-${index}`}><dt>{fact.label}</dt><dd>{fact.value}</dd></div>)}</dl>}</div>
</header>
<StoryImage image={hero} className="ac-case-hero" />
{showChapterLinks && chapters.length > 0 && <nav className="ac-case-chapter-links" aria-label={`${title} chapters`}><span>IN THIS STORY</span><ol>{chapters.map((chapter, index) => <li key={chapter.id}><a href={`#${id}-${chapter.id}`}><span aria-hidden="true">{String(index + 1).padStart(2, '0')}</span>{chapter.title}</a></li>)}</ol></nav>}
<div className="ac-case-chapters">{chapters.map((chapter, index) => <section key={chapter.id} id={`${id}-${chapter.id}`} className="ac-case-chapter" aria-labelledby={`${id}-${chapter.id}-title`}>
<div className="ac-case-chapter-text"><header><span className="ac-case-index" aria-hidden="true">/{String(index + 1).padStart(2, '0')}</span><h3 id={`${id}-${chapter.id}-title`}>{chapter.title}</h3></header><div className="ac-case-prose">{chapter.paragraphs.map((paragraph, paragraphIndex) => <p key={paragraphIndex}>{paragraph}</p>)}</div></div>
{chapter.image && <StoryImage image={chapter.image} className="ac-case-chapter-image" />}
</section>)}</div>
{outcomes.length > 0 && <section className="ac-case-outcomes" aria-label="Project outcomes"><header><span>IN THE DETAILS</span><h3>{outcomesTitle}</h3>{outcomeNote && <p>{outcomeNote}</p>}</header><dl>{outcomes.map((outcome, index) => <div key={`${outcome.label}-${index}`}><dt>{outcome.label}</dt><dd>{outcome.value}</dd></div>)}</dl></section>}
{nextProject && <a className="ac-case-next" href={nextProject.href}>{nextProject.image && <img src={nextProject.image.src} alt={nextProject.image.alt} loading="lazy" />}<div><span>{nextProject.label ?? 'NEXT PROJECT'}</span><h3>{nextProject.title}</h3></div><span className="ac-case-next-arrow" aria-hidden="true">↗</span></a>}
</article>
}
Installation
Run this in your shadcn-configured React project. The component and stylesheet are added together.
npx shadcn@latest add http://localhost:5173/r/case-study-story.jsonThis command uses the current site address. For localhost, keep this preview running on the same computer. Files follow your configured components alias.
Usage
Import the component and make the details your own.
import { CaseStudyStory } from '@/components/anticraft/CaseStudyStory';
export function Example() {
return (
<CaseStudyStory
eyebrow="RESIDENTIAL / CONCEPT STUDY"
title={'A home,\nin its element.'}
intro="A house open to the light, grounded in its materials, and made for the small rituals of everyday life."
facts={[
{ label: 'Project', value: 'Hearth House' },
{ label: 'Location', value: 'Coastal New South Wales' },
{ label: 'Scope', value: 'Architecture & interiors' },
{ label: 'Status', value: 'Concept / 2026' },
]}
hero={{ src: '/images/house.jpg', alt: 'A low timber house opening onto a coastal garden', caption: '01 / HEARTH HOUSE' }}
chapters={[
{ id: 'challenge', title: 'More room for living.', paragraphs: ['The brief began with a feeling: a place to slow down while keeping the landscape as the main event.'] },
{ id: 'approach', title: 'Let the site lead.', paragraphs: ['We brought the living spaces to the light, with deep eaves and a simple material palette.'], image: { src: '/images/terrace.jpg', alt: 'A shaded terrace between the living room and garden', caption: '02 / THE LIVING EDGE' } },
]}
outcomesTitle="The project in numbers."
outcomes={[
{ value: '186', label: 'm² considered footprint' },
{ value: '03', label: 'core material families' },
]}
outcomeNote="Illustrative concept specifications. Replace with your verified project outcomes."
nextProject={{ title: 'Headland Retreat', href: '/work/headland-retreat', label: 'NEXT PROJECT', image: { src: '/images/headland.jpg', alt: 'A retreat above the sea' } }}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
title / intro | string | Required | The visible project heading and introductory paragraph. Title newlines are preserved, with additional natural wrapping when needed. |
hero | CaseStudyImage | Required | A large project image with {src,alt,caption?}. Provide your own local or permitted image and meaningful alt text. Desktop uses a broad cover crop; narrow components use a taller crop. Keep essential project facts in the adjacent text. |
chapters | CaseStudyChapter[] | Required | Ordered story chapters with unique id, title, paragraphs: string[], and optional image: CaseStudyImage. Every chapter stays readable in the document. Optional images are visible semantic figures, with their own alt and caption. |
eyebrow | string | 'SELECTED WORK' | A short project category, series, or date above the display title. |
facts | CaseStudyFact[] | [] | Useful project facts with {label,value}, rendered as a semantic description list. The two-column fact grid follows the introduction on smaller containers. |
outcomes | CaseStudyOutcome[] | [] | Project numbers or results with {value,label}, rendered as a description list. Supply verified claims and units appropriate to your project. An empty array omits the entire outcomes panel. |
outcomesTitle / outcomeNote | string | 'The outcome.' / undefined | The outcomes heading and optional qualification or explanation. Heading newlines are preserved. Use outcomeNote for scope, measurement period, or the fact that sample data is illustrative. |
nextProject | CaseStudyNextProject | undefined | A native project link with {title,href,label?,image?}. Optional image uses CaseStudyImage. The whole surface is one link, without nested buttons. The demo jumps to an actual short next-project introduction; your site should use its real project URL. |
showChapterLinks | boolean | true | Show native anchor links to the supplied chapters. IDs include a React-generated instance prefix so multiple stories can coexist. Anchor scrolling follows the browser and nearest scrollable ancestor; no JavaScript scroll capture is used. |
className | string | '' | Additional classes. Theme --ac-case-surface, --ac-case-ink, --ac-case-muted, --ac-case-line and --ac-case-accent. Layout adapts to its own container, with a single-column story below 650px and a centered maximum width of 1920px. No section uses a fixed viewport height. |
Motion & accessibility
The story is a normal, fully readable document with native chapter anchors and a real next-project link. Images stay still; the only decorative motion is a small arrow movement on the next-project hover, removed for reduced motion. Facts and outcomes are semantic description lists. No content is hidden behind scroll progress, hover, or an animation. The demo is an original fictional architectural concept using Anticraft’s local generated imagery; its dimensions and material counts are illustrative design-study specifications, not performance claims. Replace the example image paths, project text, outcome data, and next-project destination with your own.
Use semantic content and meaningful labels in your composition. Check contrast and keyboard behavior with your own styles and content.
Inspiration
The complete project-story structure informed the content model. Anticraft’s original ivory-and-olive layout pairs large editorial typography with a cinematic image, asymmetric chapters, project specifications, and a native next-project link.
Visit Shadcnblocks — Case StudyThe component source and demo artwork are original to Anticraft UI. The linked site is the design or content reference.