Problem / Solution
Two-panel narrative layout with Problem and Solution components.
---
layout: problem-solution
title: Why Campfire
---
<Problem>
Decks rot inside proprietary tools — unversioned, undiffable,
and impossible for coding agents to help with.
</Problem>
<Solution>
The repo is the deck. Files diff, agents edit safely, and
`camp` serves a live presentation shell.
</Solution>Installation
Installs the layout plus the Problem and Solution panel components.
camp add problem-solutionOr with any shadcn-compatible CLI:
bunx shadcn@latest add https://campfire-deck.vercel.app/r/problem-solution.jsonCopy the three files. They import cn from @/lib/utils — scaffolded by
camp init, or camp add utils:
import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";
export type ProblemSolutionLayoutProps = ComponentProps<"main"> & {
/** From the slide's `title` frontmatter. */
title?: string;
};
/** Pair with the Problem and Solution components installed alongside:
*
* ```mdx
* <Problem>Decks rot in proprietary tools.</Problem>
* <Solution>Slides are files; the repo is the deck.</Solution>
* ```
*/
export default function ProblemSolutionLayout({
title,
className,
children,
...props
}: ProblemSolutionLayoutProps) {
return (
<main
className={cn("flex h-full flex-col gap-10 p-24", className)}
data-slot="problem-solution-layout"
{...props}
>
{title ? (
<h1 className="font-bold font-heading text-6xl tracking-tight">
{title}
</h1>
) : null}
<div
className="grid flex-1 grid-cols-2 items-stretch gap-10"
data-slot="problem-solution-layout-panels"
>
{children}
</div>
</main>
);
}import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";
export type ProblemProps = ComponentProps<"section">;
/** The "before" panel of a problem/solution narrative. */
export default function Problem({
className,
children,
...props
}: ProblemProps) {
return (
<section
className={cn(
"flex flex-col gap-4 rounded-2xl border-2 border-foreground/15 p-10",
className
)}
data-slot="problem"
{...props}
>
<span className="font-semibold text-foreground/50 text-sm uppercase tracking-[0.2em]">
Problem
</span>
<div className="text-2xl leading-relaxed">{children}</div>
</section>
);
}import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";
export type SolutionProps = ComponentProps<"section">;
/** The "after" panel of a problem/solution narrative. */
export default function Solution({
className,
children,
...props
}: SolutionProps) {
return (
<section
className={cn(
"flex flex-col gap-4 rounded-2xl border-2 border-primary/50 bg-primary/5 p-10",
className
)}
data-slot="solution"
{...props}
>
<span className="font-semibold text-primary text-sm uppercase tracking-[0.2em]">
Solution
</span>
<div className="text-2xl leading-relaxed">{children}</div>
</section>
);
}Usage
The layout is a two-column grid; Problem and Solution fill one column
each. They are ordinary components — you can also use them outside this
layout.
---
layout: problem-solution
title: Why Campfire
---
<Problem>Decks rot in proprietary tools.</Problem>
<Solution>Slides are files; the repo is the deck.</Solution>A generic two-column slide without the problem/solution framing is the Split layout.
API Reference
The layout follows the layout contract and exposes
data-slot="problem-solution-layout" and problem-solution-layout-panels:
Prop
Type
Problem and Solution extend ComponentProps<"section"> (exposing
data-slot="problem" / data-slot="solution") and take only children:
Prop
Type