diff --git a/docs/app/(home)/openclaw-os/page.tsx b/docs/app/(home)/openclaw-os/page.tsx
index d0de267ef..36200a61c 100644
--- a/docs/app/(home)/openclaw-os/page.tsx
+++ b/docs/app/(home)/openclaw-os/page.tsx
@@ -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";
@@ -115,7 +114,6 @@ export default function OpenClawOSPage() {
windowsInstallCommand={WINDOWS_INSTALL_COMMAND}
/>
-
diff --git a/docs/app/(home)/page.module.css b/docs/app/(home)/page.module.css
index e7d6e4886..b3a3f0319 100644
--- a/docs/app/(home)/page.module.css
+++ b/docs/app/(home)/page.module.css
@@ -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 {
diff --git a/docs/app/(home)/page.tsx b/docs/app/(home)/page.tsx
index b49a4e581..d7946a83c 100644
--- a/docs/app/(home)/page.tsx
+++ b/docs/app/(home)/page.tsx
@@ -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";
@@ -22,14 +21,12 @@ export default function HomePage() {
-
-
diff --git a/docs/app/(home)/projects/page.module.css b/docs/app/(home)/projects/page.module.css
index 5253edc9f..569857647 100644
--- a/docs/app/(home)/projects/page.module.css
+++ b/docs/app/(home)/projects/page.module.css
@@ -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 {
diff --git a/docs/app/(home)/projects/page.tsx b/docs/app/(home)/projects/page.tsx
index 949b4cdba..ddfa07fdb 100644
--- a/docs/app/(home)/projects/page.tsx
+++ b/docs/app/(home)/projects/page.tsx
@@ -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";
@@ -331,8 +330,6 @@ export default function ProjectsPage() {
-
-
@@ -388,8 +385,6 @@ export default function ProjectsPage() {
})}
-
-
diff --git a/docs/app/(home)/sections/Footer/Footer.module.css b/docs/app/(home)/sections/Footer/Footer.module.css
index 3f4e01dc4..745488d81 100644
--- a/docs/app/(home)/sections/Footer/Footer.module.css
+++ b/docs/app/(home)/sections/Footer/Footer.module.css
@@ -12,6 +12,7 @@
.handcraftedSection {
width: 100%;
padding-inline: 1.25rem;
+ background: var(--openui-foreground);
}
.handcraftedContainer {
diff --git a/docs/app/(home)/sections/Footer/Footer.tsx b/docs/app/(home)/sections/Footer/Footer.tsx
index 727642e2a..0616c76ba 100644
--- a/docs/app/(home)/sections/Footer/Footer.tsx
+++ b/docs/app/(home)/sections/Footer/Footer.tsx
@@ -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";
// ---------------------------------------------------------------------------
@@ -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 (
+
);
}
diff --git a/docs/app/blog/page.tsx b/docs/app/blog/page.tsx
index e3661d00e..583b7473d 100644
--- a/docs/app/blog/page.tsx
+++ b/docs/app/blog/page.tsx
@@ -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 (
-
-
Blog
-
- {posts.map((post) => (
-
-
-
- {post.data.title}
-
-
+
+
+
+
+ Blog
+
+
+
+
+
+
+ {posts.map((post) => (
+
+
+
+ {post.data.title}
+
+ {post.data.featured && (
+
+ Featured
+
+ )}
+
+
+ {post.data.description}
+
+
+
+ {post.data.author}
+
+ ยท
+
+
+
+ ))}
-
{post.data.description}
-
- ))}
-
-
+
+
+
+
+
);
}
diff --git a/docs/content/blog/state-of-generative-ui-report.mdx b/docs/content/blog/state-of-generative-ui-report.mdx
index e7d257950..06500c99c 100644
--- a/docs/content/blog/state-of-generative-ui-report.mdx
+++ b/docs/content/blog/state-of-generative-ui-report.mdx
@@ -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.
diff --git a/docs/content/blog/stop-making-ai-write-json.mdx b/docs/content/blog/stop-making-ai-write-json.mdx
index 440c388f2..8b42be4c4 100644
--- a/docs/content/blog/stop-making-ai-write-json.mdx
+++ b/docs/content/blog/stop-making-ai-write-json.mdx
@@ -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.
diff --git a/docs/source.config.ts b/docs/source.config.ts
index 6f5903f2b..e7795bbf2 100644
--- a/docs/source.config.ts
+++ b/docs/source.config.ts
@@ -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(),
}),
});