Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions docs/app/(home)/openclaw-os/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Metadata } from "next";
import styles from "../page.module.css";
import { FeaturesSection, OPENCLAW_FEATURES } from "../sections/FeaturesSection/FeaturesSection";
import { Footer } from "../sections/Footer/Footer";
import { GradientDivider } from "../sections/GradientDivider/GradientDivider";
import { HeroSection } from "../sections/HeroSection/HeroSection";
import heroStyles from "../sections/HeroSection/HeroSection.module.css";
import { PossibilitiesSection } from "../sections/PossibilitiesSection/PossibilitiesSection";
Expand Down Expand Up @@ -115,7 +114,6 @@ export default function OpenClawOSPage() {
windowsInstallCommand={WINDOWS_INSTALL_COMMAND}
/>
</div>
<GradientDivider direction="up" />
</div>
<Footer />
</div>
Expand Down
11 changes: 8 additions & 3 deletions docs/app/(home)/page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@
.contentSection {
position: relative;
margin-top: 4rem;
background: var(--openui-background);
background: linear-gradient(
to bottom,
var(--openui-foreground),
var(--openui-background) 10rem,
var(--openui-background) calc(100% - 10rem),
var(--openui-foreground)
);
}

.contentShell {
display: flex;
flex-direction: column;
gap: 7.5rem;
padding-block: 5rem;
background: var(--openui-background);
padding-block: 8rem;
}

.contentShellTight {
Expand Down
3 changes: 0 additions & 3 deletions docs/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import styles from "./page.module.css";
import { CompatibilitySection } from "./sections/CompatibilitySection/CompatibilitySection";
import { FeaturesSection } from "./sections/FeaturesSection/FeaturesSection";
import { Footer } from "./sections/Footer/Footer";
import { GradientDivider } from "./sections/GradientDivider/GradientDivider";
import { HeroSection } from "./sections/HeroSection/HeroSection";
import { PossibilitiesSection } from "./sections/PossibilitiesSection/PossibilitiesSection";
import { ShiroMascot } from "./sections/ShiroMascot/ShiroMascot";
Expand All @@ -22,14 +21,12 @@ export default function HomePage() {
<StepsSection />
</div>
<div className={styles.contentSection}>
<GradientDivider direction="down" />
<div className={styles.contentShell}>
<PossibilitiesSection />
<CompatibilitySection />
<FeaturesSection />
<TweetWallSection />
</div>
<GradientDivider direction="up" />
</div>
<Footer />
</div>
Expand Down
10 changes: 8 additions & 2 deletions docs/app/(home)/projects/page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,17 @@

.contentSection {
position: relative;
background: var(--openui-background);
background: linear-gradient(
to bottom,
var(--openui-foreground),
var(--openui-background) 10rem,
var(--openui-background) calc(100% - 10rem),
var(--openui-foreground)
);
}

.directorySection {
padding: 2rem var(--home-section-padding-inline, 1.25rem) 2rem;
padding: 5rem var(--home-section-padding-inline, 1.25rem) 5rem;
}

.sectionHeader {
Expand Down
5 changes: 0 additions & 5 deletions docs/app/(home)/projects/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import type { Metadata } from "next";
import type { ComponentType } from "react";
import { PillLink } from "../components/Button/Button";
import { Footer } from "../sections/Footer/Footer";
import { GradientDivider } from "../sections/GradientDivider/GradientDivider";
import styles from "./page.module.css";

type ProjectStatus = "Official" | "Community";
Expand Down Expand Up @@ -331,8 +330,6 @@ export default function ProjectsPage() {
</section>

<div className={styles.contentSection}>
<GradientDivider direction="down" compact />

<section className={styles.directorySection} id="directory">
<div className={styles.sectionHeader}>
<div>
Expand Down Expand Up @@ -388,8 +385,6 @@ export default function ProjectsPage() {
})}
</div>
</section>

<GradientDivider direction="up" compact />
</div>

<section className={styles.submitSection}>
Expand Down
1 change: 1 addition & 0 deletions docs/app/(home)/sections/Footer/Footer.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
.handcraftedSection {
width: 100%;
padding-inline: 1.25rem;
background: var(--openui-foreground);
}

.handcraftedContainer {
Expand Down
9 changes: 7 additions & 2 deletions docs/app/(home)/sections/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import mascotDarkSvgPaths from "@/imports/svg-mascot-dark";
import svgPaths from "@/imports/svg-urruvoh2be";
import mascotSvgPaths from "@/imports/svg-xeurqn3j1r";
import { useId } from "react";
import { useTheme } from "next-themes";
import { useEffect, useId, useState } from "react";
import styles from "./Footer.module.css";

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -193,7 +194,11 @@ function HandcraftedMascot({ isDark }: { isDark: boolean }) {
// ---------------------------------------------------------------------------

export function Footer() {
const isDark = false;
const { resolvedTheme } = useTheme();
// Resolve the theme only after mount so the server and first client render match
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
const isDark = mounted && resolvedTheme === "dark";

return (
<footer className={styles.footer}>
Expand Down
5 changes: 5 additions & 0 deletions docs/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PillLink } from "@/app/(home)/components/Button/Button";
import { Footer } from "@/app/(home)/sections/Footer/Footer";
import { blog } from "@/lib/source";
import { getMDXComponents } from "@/mdx-components";
import { TOCItems } from "fumadocs-ui/components/toc/default";
Expand All @@ -23,6 +24,8 @@ export default async function BlogPostPage(props: { params: Promise<{ slug: stri

return (
<TOCProvider toc={page.data.toc}>
{/* Blend from the menu bar's surface color into the page background, then back into the footer's surface */}
<div className="min-h-screen bg-[linear-gradient(to_bottom,var(--openui-foreground),var(--openui-background)_10rem,var(--openui-background)_calc(100%_-_10rem),var(--openui-foreground))]">
<main className="mx-auto flex w-full max-w-[1200px] gap-4 px-4 pt-16 pb-40 lg:gap-28 lg:pr-8 min-[1249px]:pl-0 min-[1024px]:max-[1248px]:pl-8">
<aside className="hidden w-56 shrink-0 lg:block">
<div className="sticky top-24">
Expand Down Expand Up @@ -56,6 +59,8 @@ export default async function BlogPostPage(props: { params: Promise<{ slug: stri
</div>
</div>
</main>
</div>
<Footer />
</TOCProvider>
);
}
Expand Down
83 changes: 61 additions & 22 deletions docs/app/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,72 @@
import { blog } from "@/lib/source";
import Link from "next/link";
import { Footer } from "../(home)/sections/Footer/Footer";

function formatDate(date: string | Date) {
return new Date(date).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
}

export default function BlogIndex() {
const posts = blog.getPages().sort((a, b) => {
const byDate = blog.getPages().sort((a, b) => {
return new Date(b.data.date).getTime() - new Date(a.data.date).getTime();
});
// Featured posts lead so their full-width cards sit on top of the grid
const posts = [
...byDate.filter((post) => post.data.featured),
...byDate.filter((post) => !post.data.featured),
];

return (
<main className="mx-auto w-full max-w-[800px] flex-1 px-4 py-16 md:px-8">
<h1 className="mb-12 text-4xl font-bold">Blog</h1>
<div className="flex flex-col divide-y divide-fd-border">
{posts.map((post) => (
<Link
key={post.url}
href={post.url}
className="group flex flex-col gap-1 py-6 no-underline"
>
<div className="flex items-baseline justify-between gap-4">
<h2 className="text-lg font-semibold transition-colors group-hover:text-fd-primary">
{post.data.title}
</h2>
<time className="shrink-0 text-sm text-fd-muted-foreground">
{new Date(post.data.date).toLocaleDateString()}
</time>
<div className="flex min-h-screen flex-col bg-[var(--blog-surface)] [--blog-surface:#fff] [[data-theme=dark]_&]:[--blog-surface:#171717]">
<main className="flex flex-1 flex-col">
<div className="mx-auto w-full max-w-[960px] px-4 pb-12 pt-16 md:px-8">
<h1 className="text-4xl font-bold text-[color:var(--openui-text-neutral-primary)]">
Blog
</h1>
</div>

<section className="bg-[linear-gradient(to_bottom,var(--blog-surface),var(--openui-background)_10rem,var(--openui-background)_calc(100%_-_10rem),var(--blog-surface))]">
<div className="mx-auto w-full max-w-[960px] px-4 py-24 md:px-8">
<div className="grid grid-cols-1 gap-5 md:auto-rows-fr md:grid-cols-2">
{posts.map((post) => (
<Link
key={post.url}
href={post.url}
className={`group flex min-h-[15rem] flex-col rounded-[var(--openui-radius-4xl)] border border-[var(--openui-border-default)] bg-[var(--openui-foreground)] p-6 no-underline shadow-[var(--openui-shadow-m)] transition-[border-color,box-shadow] hover:border-[var(--openui-border-interactive-emphasis)] hover:shadow-[var(--openui-shadow-l)] ${
post.data.featured ? "md:col-span-2" : ""
}`}
>
<div className="flex items-start justify-between gap-3">
<h2 className="text-lg font-semibold leading-snug text-[color:var(--openui-text-neutral-primary)]">
{post.data.title}
</h2>
{post.data.featured && (
<span className="inline-flex h-[1.625rem] shrink-0 items-center rounded-lg bg-[color-mix(in_srgb,#7c3aed_14%,transparent)] px-2 text-xs font-semibold uppercase tracking-wide text-[#7c3aed] [[data-theme=dark]_&]:text-[#a78bfa]">
Featured
</span>
)}
</div>
<p className="mt-3 line-clamp-4 text-sm leading-relaxed text-[color:var(--openui-text-neutral-secondary)]">
{post.data.description}
</p>
<div className="mt-auto flex items-center gap-2 pt-5 text-sm text-[color:var(--openui-text-neutral-secondary)]">
<span className="font-medium text-[color:var(--openui-text-neutral-primary)]">
{post.data.author}
</span>
<span aria-hidden="true">·</span>
<time>{formatDate(post.data.date)}</time>
</div>
</Link>
))}
</div>
<p className="text-sm text-fd-muted-foreground">{post.data.description}</p>
</Link>
))}
</div>
</main>
</div>
</section>
</main>
<Footer />
</div>
);
}
1 change: 1 addition & 0 deletions docs/content/blog/state-of-generative-ui-report.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ title: "The State of Generative UI in 2026: Transports, Formats, and Tradeoffs"
description: "A 2026 survey of Generative UI: AG-UI vs MCP-Apps, Static vs Declarative vs Open-Ended generation, plus token benchmarks for OpenUI, A2UI, and json-render."
author: Thesys Engineering Team
date: 2026-06-10
featured: true
---

Generative UI (interfaces generated by an agent at runtime rather than built ahead of time) has settled into a recognizable stack in 2026. This report surveys that stack: the protocols (AG-UI, MCP-Apps), the generation formats (OpenUI, A2UI, json-render, raw HTML), and the measurements that should inform a choice between them.
Expand Down
1 change: 1 addition & 0 deletions docs/content/blog/stop-making-ai-write-json.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ title: "Stop making AI write JSON - Why we built OpenUI"
description: "We shipped a JSON-based Generative UI SDK. When we tried to add interactivity, JSON fell apart. A full programming language was worse. This is the story behind why we built OpenUI."
date: "2026-04-10"
author: "Thesys Engineering Team"
featured: true
---

JSON is a data format pretending to be a language. When you need a UI that can fetch data, manage state, and respond to user input, that distinction stops being theoretical.
Expand Down
1 change: 1 addition & 0 deletions docs/source.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const blogPosts = defineCollections({
schema: pageSchema.extend({
author: z.string(),
date: z.string().date().or(z.date()),
featured: z.boolean().optional(),
}),
});

Expand Down
Loading