Skip to content

Commit 53b7d13

Browse files
maxprilutskiyclaude
andcommitted
feat: add Markdoc-authored bento grid to the marketing page
Introduce bento, bento-tile, bento-video, bento-payment, and bento-code tags so content editors can compose a heterogeneous tile grid from Markdown alone. Three tiles ship: a video placeholder (wide), a mock payment button (client component with idle/loading/paid states), and a full-width embed code snippet. Home route now renders the markdoc body in a wider section below the hero. Tile sizes are controlled from markdown via the colSpan attribute, so adding or resizing tiles needs no TSX changes. Translations regenerated for es/fr/de. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 104eeaa commit 53b7d13

12 files changed

Lines changed: 351 additions & 5 deletions

File tree

i18n.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ version: 1
22
checksums:
33
539d38021b42abf46911ae19ad0cb996:
44
paragraph-0: 675c5577c7e4c945a1a33ca50ab77428
5+
heading-0: 734fe828e8be781431f0a41f5adc92b9
6+
fence-0: 27c6588253021c29ff7829652f29f524
57
fm-attr-title: 54f4edf22d864ec9cac6f3771f3b6cc3
68
fm-attr-description: 5d33312137e459288cf54d85ab250e24
79
fm-attr-badge: a39cdbb8f362c0d3754a098416fb9a8b

src/app/[lang]/page.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ export default async function Home(props: PageProps<"/[lang]">) {
3030
const { home } = await getMessages(lang);
3131

3232
return (
33-
<main className="mx-auto flex w-full max-w-3xl flex-1 flex-col items-center justify-center gap-8 px-6 py-10 text-center md:py-12">
33+
<>
34+
<main className="mx-auto flex w-full max-w-3xl flex-col items-center gap-8 px-6 py-16 text-center md:py-24">
3435
{badge ? (
3536
<Link
3637
href={`/${lang}/blog`}
@@ -68,11 +69,17 @@ export default async function Home(props: PageProps<"/[lang]">) {
6869
</Link>
6970
<p className="text-sm text-muted-foreground">{home.noCreditCardRequired}</p>
7071
</div>
71-
72-
<div className="w-full max-w-xl text-left">
73-
{renderMarkdoc(doc.content)}
74-
</div>
7572
</main>
73+
74+
<section
75+
className={[
76+
"mx-auto w-full max-w-6xl px-6 pb-20 md:pb-28",
77+
"[&_h2]:font-heading [&_h2]:text-3xl [&_h2]:font-semibold [&_h2]:text-foreground [&_h2]:md:text-4xl",
78+
].join(" ")}
79+
>
80+
{renderMarkdoc(doc.content)}
81+
</section>
82+
</>
7683
);
7784
}
7885

src/components/markdoc/Bento.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { ReactNode } from "react";
2+
3+
export function Bento({ children }: { children?: ReactNode }) {
4+
return (
5+
<section className="my-10 grid grid-cols-1 gap-4 sm:grid-cols-3 md:my-16 md:gap-5">
6+
{children}
7+
</section>
8+
);
9+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import type { ReactNode } from "react";
2+
3+
export function BentoCode({
4+
language,
5+
children,
6+
}: {
7+
language?: string;
8+
children?: ReactNode;
9+
}) {
10+
return (
11+
<div className="overflow-hidden rounded-xl border border-zinc-800 bg-zinc-950 text-zinc-100">
12+
<div className="flex items-center justify-between border-b border-zinc-800 px-4 py-2.5">
13+
<div className="flex gap-1.5">
14+
<span aria-hidden className="h-2.5 w-2.5 rounded-full bg-zinc-700" />
15+
<span aria-hidden className="h-2.5 w-2.5 rounded-full bg-zinc-700" />
16+
<span aria-hidden className="h-2.5 w-2.5 rounded-full bg-zinc-700" />
17+
</div>
18+
{language && (
19+
<span className="font-mono text-xs uppercase tracking-wide text-zinc-500">
20+
{language}
21+
</span>
22+
)}
23+
</div>
24+
<div className="overflow-x-auto [&_code]:bg-transparent [&_code]:font-mono [&_code]:text-sm [&_code]:leading-relaxed [&_code]:text-zinc-100 [&_pre]:bg-transparent [&_pre]:p-4">
25+
{children}
26+
</div>
27+
</div>
28+
);
29+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { Check, Loader2 } from "lucide-react";
5+
6+
type Status = "idle" | "loading" | "success";
7+
8+
export function BentoPayment({
9+
amount = "$40.00",
10+
label = "Pay now",
11+
}: {
12+
amount?: string;
13+
label?: string;
14+
}) {
15+
const [status, setStatus] = useState<Status>("idle");
16+
17+
function handleClick() {
18+
if (status !== "idle") return;
19+
setStatus("loading");
20+
setTimeout(() => setStatus("success"), 1100);
21+
setTimeout(() => setStatus("idle"), 3200);
22+
}
23+
24+
return (
25+
<div className="flex flex-col gap-3 rounded-xl bg-muted p-5">
26+
<div className="flex items-center justify-between">
27+
<span className="text-sm text-muted-foreground">Amount due</span>
28+
<span className="font-mono text-xl font-semibold text-foreground">
29+
{amount}
30+
</span>
31+
</div>
32+
<button
33+
type="button"
34+
onClick={handleClick}
35+
disabled={status !== "idle"}
36+
className="inline-flex h-11 items-center justify-center gap-2 rounded-full bg-foreground px-5 text-sm font-medium text-background transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-80"
37+
>
38+
{status === "idle" && (
39+
<>
40+
{label}
41+
<span aria-hidden className="text-base leading-none"></span>
42+
</>
43+
)}
44+
{status === "loading" && (
45+
<>
46+
<Loader2 className="h-4 w-4 animate-spin" />
47+
Processing…
48+
</>
49+
)}
50+
{status === "success" && (
51+
<>
52+
<Check className="h-4 w-4" />
53+
Paid
54+
</>
55+
)}
56+
</button>
57+
</div>
58+
);
59+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { ReactNode } from "react";
2+
import { cn } from "@/lib/utils";
3+
4+
const COL_CLASSES: Record<number, string> = {
5+
1: "sm:col-span-1",
6+
2: "sm:col-span-2",
7+
3: "sm:col-span-3",
8+
};
9+
10+
const ROW_CLASSES: Record<number, string> = {
11+
1: "row-span-1",
12+
2: "sm:row-span-2",
13+
};
14+
15+
type Props = {
16+
colSpan?: number;
17+
rowSpan?: number;
18+
title?: string;
19+
description?: string;
20+
children?: ReactNode;
21+
};
22+
23+
export function BentoTile({
24+
colSpan = 1,
25+
rowSpan = 1,
26+
title,
27+
description,
28+
children,
29+
}: Props) {
30+
const cols = COL_CLASSES[colSpan] ?? COL_CLASSES[1];
31+
const rows = ROW_CLASSES[rowSpan] ?? ROW_CLASSES[1];
32+
33+
return (
34+
<article
35+
className={cn(
36+
"flex flex-col gap-5 rounded-2xl border border-border bg-card p-5 md:p-6",
37+
cols,
38+
rows,
39+
)}
40+
>
41+
{(title || description) && (
42+
<header className="flex flex-col gap-1.5">
43+
{title && (
44+
<h3 className="font-heading text-lg font-semibold text-foreground md:text-xl">
45+
{title}
46+
</h3>
47+
)}
48+
{description && (
49+
<p className="text-sm text-muted-foreground">{description}</p>
50+
)}
51+
</header>
52+
)}
53+
<div className="flex flex-1 flex-col justify-end">{children}</div>
54+
</article>
55+
);
56+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Play } from "lucide-react";
2+
3+
export function BentoVideo({ label }: { label?: string }) {
4+
return (
5+
<div className="relative flex aspect-video w-full items-center justify-center overflow-hidden rounded-xl bg-gradient-to-br from-zinc-900 via-zinc-800 to-zinc-700">
6+
<span
7+
aria-hidden
8+
className="absolute inset-0 bg-[radial-gradient(circle_at_25%_15%,rgba(255,255,255,0.12),transparent_55%)]"
9+
/>
10+
<span
11+
aria-hidden
12+
className="absolute inset-x-0 bottom-0 h-20 bg-gradient-to-t from-black/60 to-transparent"
13+
/>
14+
<button
15+
type="button"
16+
aria-label="Play video"
17+
className="relative flex h-14 w-14 items-center justify-center rounded-full bg-white/95 text-zinc-900 shadow-xl transition hover:scale-105 md:h-16 md:w-16"
18+
>
19+
<Play className="h-6 w-6 translate-x-0.5 fill-current" />
20+
</button>
21+
{label && (
22+
<span className="absolute bottom-3 left-4 text-xs font-medium text-white/90">
23+
{label}
24+
</span>
25+
)}
26+
</div>
27+
);
28+
}

src/content/de/pages/home.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,33 @@ badge: Cal.com bringt v6.4 heraus
99
{% callout type="note" %}
1010
Diese Website wurde in Markdoc erstellt und ist auf Englisch, Spanisch, Französisch und Deutsch verfügbar.
1111
{% /callout %}
12+
13+
## …und vieles mehr
14+
15+
{% bento %}
16+
{% bento-tile
17+
colSpan=2
18+
title="See scheduling in motion"
19+
description="Watch how fast a booking comes together." %}
20+
{% bento-video label="Product tour · 1:24" /%}
21+
{% /bento-tile %}
22+
23+
{% bento-tile
24+
colSpan=1
25+
title="Accept payments"
26+
description="Charge for a meeting in one click." %}
27+
{% bento-payment amount="$40.00" label="Pay now" /%}
28+
{% /bento-tile %}
29+
30+
{% bento-tile
31+
colSpan=3
32+
title="Embed anywhere"
33+
description="Drop Cal.com into any site with one snippet." %}
34+
{% bento-code language="html" %}
35+
```html
36+
<script src="https://app.cal.com/embed.js"></script>
37+
<div data-cal-link="you/meeting"></div>
38+
```
39+
{% /bento-code %}
40+
{% /bento-tile %}
41+
{% /bento %}

src/content/en/pages/home.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,26 @@ badge: Cal.com launches v6.4
77
{% callout type="note" %}
88
This site is authored in Markdoc and available in English, Spanish, French, and German.
99
{% /callout %}
10+
11+
## …and so much more
12+
13+
{% bento %}
14+
15+
{% bento-tile colSpan=2 title="See scheduling in motion" description="Watch how fast a booking comes together." %}
16+
{% bento-video label="Product tour · 1:24" /%}
17+
{% /bento-tile %}
18+
19+
{% bento-tile colSpan=1 title="Accept payments" description="Charge for a meeting in one click." %}
20+
{% bento-payment amount="$40.00" label="Pay now" /%}
21+
{% /bento-tile %}
22+
23+
{% bento-tile colSpan=3 title="Embed anywhere" description="Drop Cal.com into any site with one snippet." %}
24+
{% bento-code language="html" %}
25+
```html
26+
<script src="https://app.cal.com/embed.js"></script>
27+
<div data-cal-link="you/meeting"></div>
28+
```
29+
{% /bento-code %}
30+
{% /bento-tile %}
31+
32+
{% /bento %}

src/content/es/pages/home.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,33 @@ badge: Cal.com lanza v6.4
99
{% callout type="note" %}
1010
Este sitio está escrito en Markdoc y está disponible en inglés, español, francés y alemán.
1111
{% /callout %}
12+
13+
## …y mucho más
14+
15+
{% bento %}
16+
{% bento-tile
17+
colSpan=2
18+
title="See scheduling in motion"
19+
description="Watch how fast a booking comes together." %}
20+
{% bento-video label="Product tour · 1:24" /%}
21+
{% /bento-tile %}
22+
23+
{% bento-tile
24+
colSpan=1
25+
title="Accept payments"
26+
description="Charge for a meeting in one click." %}
27+
{% bento-payment amount="$40.00" label="Pay now" /%}
28+
{% /bento-tile %}
29+
30+
{% bento-tile
31+
colSpan=3
32+
title="Embed anywhere"
33+
description="Drop Cal.com into any site with one snippet." %}
34+
{% bento-code language="html" %}
35+
```html
36+
<script src="https://app.cal.com/embed.js"></script>
37+
<div data-cal-link="you/meeting"></div>
38+
```
39+
{% /bento-code %}
40+
{% /bento-tile %}
41+
{% /bento %}

0 commit comments

Comments
 (0)