;
+const inputs = [
+ "mode-switch",
+ "send-control",
+ "completion-palette",
+ "attachment-chip",
+ "search-field",
+];
+
+export const catalog = registry.items
+ .filter((item) =>
+ ["registry:ui", "registry:component", "registry:block"].includes(item.type),
+ )
+ .map((item) => {
+ const symbol = item.name
+ .split("-")
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
+ .join("");
+ const exampleName = `${symbol}Example` as keyof typeof examples;
+ const file = item.files[0];
+ if (!file || !examples[exampleName])
+ throw new Error(`Missing documentation example: ${item.name}`);
+ const sourcePath = file.path;
+ const group: Group =
+ item.type === "registry:block"
+ ? "Blocks"
+ : sourcePath.includes("/components/ui/")
+ ? "Foundations"
+ : inputs.includes(item.name)
+ ? "Composer controls"
+ : "Run & evidence";
+ const source = sources[`../../../${sourcePath}`] ?? "";
+ const api = source.match(/type \w+Props = [\s\S]*?\n};/)?.[0];
+ return {
+ ...item,
+ group,
+ symbol,
+ exampleName,
+ sourcePath,
+ api,
+ source,
+ Component: examples[exampleName] as ComponentType<{ density?: Density }>,
+ packagePath: sourcePath
+ .replace("packages/ui/src/", "@opencoven/ui/")
+ .replace(/\.tsx$/, ""),
+ consumerPath: (file.target ?? "")
+ .replace("@ui/", "@/components/ui/")
+ .replace("@components/", "@/components/")
+ .replace(/\.tsx$/, ""),
+ };
+ })
+ .sort(
+ (a, b) =>
+ groups.indexOf(a.group) - groups.indexOf(b.group) ||
+ a.title.localeCompare(b.title),
+ );
+
+export type CatalogEntry = (typeof catalog)[number];
+export const guides = [
+ { href: "/docs", title: "Introduction" },
+ { href: "/docs/installation", title: "Installation" },
+ { href: "/docs/theming", title: "Theming" },
+];
+export function componentHref(id: string) {
+ return `/docs/components/${id}`;
+}
+export function installCommand(id: string) {
+ return `pnpm dlx shadcn@latest add ${registryOrigin}/${id}.json`;
+}
+export function exampleCode(entry: CatalogEntry) {
+ const body =
+ exampleSource
+ .split(`export function ${entry.exampleName}(`)[1]
+ ?.split("\nexport function ")[0]
+ ?.trim() ?? "";
+ const code = `export function ${entry.exampleName}(${body}`;
+ const imports =
+ [
+ ...exampleSource.matchAll(/import \{([^}]+)\} from "@opencoven\/ui";/g),
+ ][0]?.[1] ?? "";
+ const uiImports = imports
+ .split(",")
+ .map((name) => name.trim())
+ .filter((name) => name && new RegExp(`\\b${name}\\b`).test(code));
+ return `${code.includes("useState") ? 'import { useState } from "react";\n' : ""}import { ${uiImports.join(", ")} } from "@opencoven/ui";\n\n${code}`;
+}
diff --git a/apps/specimens/src/code-block.tsx b/apps/specimens/src/code-block.tsx
new file mode 100644
index 0000000..3ffd024
--- /dev/null
+++ b/apps/specimens/src/code-block.tsx
@@ -0,0 +1,52 @@
+import { useState } from "react";
+import { Button } from "@opencoven/ui";
+import { Check, Copy, Terminal } from "lucide-react";
+
+export function CodeBlock({
+ code,
+ label = "Terminal",
+ compact = false,
+}: {
+ code: string;
+ label?: string;
+ compact?: boolean;
+}) {
+ const [status, setStatus] = useState("");
+ async function copy() {
+ try {
+ await navigator.clipboard.writeText(code);
+ setStatus("Copied");
+ } catch {
+ setStatus("Copy unavailable. Select the code to copy it.");
+ }
+ }
+ return (
+
+
+
+
+ {label}
+
+
+ {status === "Copied" ? : }
+
+
+
+ {code}
+
+
+ {status}
+
+
+ );
+}
diff --git a/apps/specimens/src/docs.tsx b/apps/specimens/src/docs.tsx
new file mode 100644
index 0000000..3a90222
--- /dev/null
+++ b/apps/specimens/src/docs.tsx
@@ -0,0 +1,580 @@
+import { useState } from "react";
+import {
+ ArrowLeft,
+ ArrowRight,
+ ArrowUpRight,
+ BookOpen,
+ Code2,
+ PanelLeft,
+ SlidersHorizontal,
+} from "lucide-react";
+import {
+ Badge,
+ Button,
+ EmptyState,
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+ buttonVariants,
+} from "@opencoven/ui";
+import {
+ catalog,
+ componentHref,
+ exampleCode,
+ groups,
+ guides,
+ installCommand,
+ repository,
+ type CatalogEntry,
+ type Density,
+} from "./catalog";
+import { CodeBlock } from "./code-block";
+
+export function Docs({
+ path,
+ query,
+ density,
+ onDensityChange,
+}: {
+ path: string;
+ query: string;
+ density: Density;
+ onDensityChange: (density: Density) => void;
+}) {
+ const [menuOpen, setMenuOpen] = useState(false);
+ const results = catalog.filter((entry) =>
+ `${entry.title} ${entry.description} ${entry.group} ${entry.meta?.examples?.join(" ") ?? ""}`
+ .toLowerCase()
+ .includes(query.trim().toLowerCase()),
+ );
+ const entry = catalog.find((item) => componentHref(item.name) === path);
+ const isCatalog = path === "/docs/components";
+ const isGuide = guides.some((guide) => guide.href === path);
+ return (
+
+
+
setMenuOpen(!menuOpen)}
+ >
+
+ Documentation menu
+
+
+
+
+
+ Docs
+ /
+ {entry ? (
+ <>
+ Components
+ /
+ {entry.title}
+ >
+ ) : isCatalog ? (
+ "Components"
+ ) : (
+ (guides.find((guide) => guide.href === path)?.title ?? "Not found")
+ )}
+
+ {entry ? (
+
+ ) : isCatalog ? (
+ <>
+
+
The collection
+
Build with good pieces.
+
+ {catalog.length} focused components. One familiar system. Find
+ what your interface needs and make it your own.
+
+
+ {results.length ? (
+ groups.map((group) => {
+ const items = results.filter((item) => item.group === group);
+ return items.length ? (
+
+
+ {group}
+ {items.length}
+
+
+
+ ) : null;
+ })
+ ) : (
+
+ )}
+ >
+ ) : isGuide ? (
+
+ ) : (
+
+ Browse components
+
+ }
+ />
+ )}
+
+
+
+ );
+}
+
+function ComponentDoc({
+ entry,
+ density,
+ onDensityChange,
+}: {
+ entry: CatalogEntry;
+ density: Density;
+ onDensityChange: (value: Density) => void;
+}) {
+ const index = catalog.indexOf(entry);
+ const previous = catalog[index - 1];
+ const next = catalog[index + 1];
+ const code = exampleCode(entry);
+ return (
+ <>
+
+
+
+
+
+ Preview
+ Code
+
+ {entry.source.includes('density?: "default" | "compact"') && (
+
+
+ Preview density
+
+ onDensityChange(event.target.value as Density)
+ }
+ >
+ Cozy
+ Compact
+
+
+ )}
+
+
+
+
+
+
+ Live component preview React · TypeScript
+
+
+
+
+
+
+
+
+ Installation
+
+ Add the component source to your project with the shadcn CLI. Its
+ registry dependencies are included automatically.
+
+
+
+ Using the package instead? Follow the{" "}
+ package setup and use{" "}
+ {entry.packagePath}.
+
+
+
+ Usage
+
+ For a registry install using the default aliases, import from your
+ local source:
+
+
+
+ The Code tab contains the exact interactive example above, using
+ package imports. Adapt those imports to your local paths when using
+ registry source.
+
+ {entry.meta?.examples?.length ? (
+
+ Supported states
+ {entry.meta.examples.map((state) => (
+
+ {state}
+
+ ))}
+
+ ) : null}
+
+
+ API reference
+
+ {entry.api
+ ? "The current public prop contract, taken directly from the component source."
+ : "This primitive forwards its underlying element or Base UI props. See the source for its composition and supported variants."}
+
+ {entry.api ? (
+
+ ) : (
+
+ Explore the full API
+
+ )}
+
+ Semantic tokens carry the visual treatment. Preserve accessible
+ labels, explicit state cues, and visible focus when customizing your
+ copy.
+
+
+
+ {previous ? (
+
+
+
+ Previous
+ {previous.title}
+
+
+ ) : (
+
+ )}
+ {next && (
+
+
+ Next
+ {next.title}
+
+
+
+ )}
+
+ >
+ );
+}
+
+function Guide({ path }: { path: string }) {
+ if (path === "/docs/installation")
+ return (
+
+
+
+ Start with your foundation
+
+ Coven UI targets React 19.2 and Tailwind CSS 4. Interactive
+ primitives use Base UI. Start in a React project with shadcn
+ configured for Base UI and working component aliases.
+
+
+ Add the Coven theme
+
+ The theme provides the semantic colors, typography, and density
+ variables used throughout the library. Review the generated
+ stylesheet, import it in your app entry, and keep only one Tailwind
+ entry import.
+
+
+
+ Add your first component
+
+
+ Registry items are copied into your project. You own them. For the
+ default aliases, blocks go into components/blocks,
+ Coven components into components, and primitives into{" "}
+ components/ui. Your components.json aliases determine
+ the exact locations.
+
+
+
+ Working with the package
+
+ The repository also exposes @opencoven/ui through its
+ workspace package. To use that route, clone the GitHub repository
+ and build the workspace. This does not assume an npm release is
+ available.
+
+
+
+ Within the workspace, add @opencoven/ui: workspace:* to
+ your app dependencies, then import the stylesheet and components:
+
+
+
+ For projects outside this workspace, the shadcn CLI is the simplest
+ source installation path.
+
+
+
+ );
+ if (path === "/docs/theming")
+ return (
+
+
+
+ Theme with meaning
+
+ Use background and foreground for the canvas, card for surfaces, and
+ presence for familiar identity and intentional accents. Define
+ tokens in your global stylesheet rather than overriding individual
+ components.
+
+
+ {["background", "card", "foreground", "presence"].map((token) => (
+
+
+ --{token}
+
+ ))}
+
+
+ Light and dark
+
+ Add or remove dark on the root element. Components
+ inherit their semantic colors without per-component dark-mode
+ classes.
+
+
+ Density is explicit
+
+ Pass density="compact" to components that support it.
+ Density changes spacing, not information hierarchy. The docs preview
+ control lets you compare supported densities.
+
+ '}
+ />
+ State is not decoration
+
+ Keep canonical tool classes stable: read, exec, write, and net have
+ distinct meanings. Statuses pair color with text and icons. Preserve
+ these cues, keyboard focus, and reduced-motion support when changing
+ your theme.
+
+
+
+ );
+ return (
+
+
+
+
+
+ This is your source code, not a black box. Take the components you
+ need, change what you want, and build something that feels like you.
+
+
+
+ Less scaffolding. More making.
+
+ Coven UI brings intent, execution, and evidence into the same visual
+ language. Start with familiar primitives, add focused controls, then
+ compose complete agent surfaces.
+
+
+ Four layers, one system
+ {groups.map((group) => (
+
+
{group}
+
+ {
+ {
+ Foundations:
+ "Buttons, inputs, menus, and the accessible primitives every product needs.",
+ "Composer controls":
+ "Express intent, choose authority, attach context, and send with confidence.",
+ "Run & evidence":
+ "Make progress, resource changes, context, and operational limits visible.",
+ Blocks:
+ "Complete composers, transcripts, session headers, and run rails.",
+ }[group]
+ }
+
+
+ ))}
+ Built to be understood
+
+ React 19.2, TypeScript, Tailwind CSS 4, and Base UI. Explicit props,
+ readable source, keyboard-ready interactions, and light and dark
+ themes. No model provider or backend is bundled into the components.
+
+
+
+ );
+}
diff --git a/apps/specimens/src/examples.tsx b/apps/specimens/src/examples.tsx
new file mode 100644
index 0000000..f0af335
--- /dev/null
+++ b/apps/specimens/src/examples.tsx
@@ -0,0 +1,518 @@
+import { useState } from "react";
+import {
+ ActivityItem,
+ AttachmentChip,
+ Badge,
+ BudgetPill,
+ Button,
+ Card,
+ CardContent,
+ CardFooter,
+ CardHeader,
+ CompletionPalette,
+ Composer,
+ ContextMeter,
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+ EmptyState,
+ ErrorState,
+ FailureSurface,
+ Input,
+ MetricDisplay,
+ ModeSwitch,
+ PlanRow,
+ Progress,
+ ResourceRow,
+ RunRail,
+ SearchField,
+ SendControl,
+ Separator,
+ SessionHeader,
+ StatusIndicator,
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+ Textarea,
+ ToolClassBadge,
+ ToolMix,
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+ TranscriptTurn,
+} from "@opencoven/ui";
+
+export function ButtonExample() {
+ const [saved, setSaved] = useState(false);
+ return (
+
+ setSaved(!saved)}>
+ {saved ? "Changes saved" : "Save changes"}
+
+ setSaved(false)}>
+ Reset
+
+ Disabled
+
+ );
+}
+
+export function BadgeExample() {
+ return (
+
+ Default
+ In progress
+ Draft
+
+ );
+}
+
+export function CardExample() {
+ const [saved, setSaved] = useState(false);
+ return (
+
+
+ A space for your next idea.
+
+ Composable surfaces. Nothing more than you need.
+
+
+
+ Your next project starts with a small building block.
+
+
+ setSaved(!saved)}>
+ {saved ? "Added to this demo" : "Try this component"}
+
+
+
+ );
+}
+
+export function DropdownMenuExample() {
+ const [selected, setSelected] = useState("Choose an action");
+ return (
+
+
+ Open menu} />
+
+
+ setSelected("Duplicate selected")}>
+ Duplicate
+
+ setSelected("Archive selected")}>
+ Archive
+
+
+
+
+
{selected}
+
+ );
+}
+
+export function InputExample({
+ density = "default",
+}: {
+ density?: "default" | "compact";
+}) {
+ return (
+
+ Project name
+
+
+ );
+}
+
+export function ProgressExample() {
+ const [value, setValue] = useState(40);
+ return (
+
+
+
setValue(value === 100 ? 0 : value + 20)}>
+ {value === 100 ? "Start again" : `Advance · ${value}%`}
+
+
+ );
+}
+
+export function SeparatorExample() {
+ return (
+
+
Intent
+
+
Execution
+
+
Evidence
+
+ );
+}
+
+export function TabsExample() {
+ return (
+
+
+ Overview
+ Activity
+
+
+ A little structure makes everything easier to find.
+
+
+ All caught up. Your next action will appear here.
+
+
+ );
+}
+
+export function TextareaExample({
+ density = "default",
+}: {
+ density?: "default" | "compact";
+}) {
+ return (
+
+ Your instructions
+
+
+ );
+}
+
+export function TooltipExample() {
+ return (
+
+ Hover or focus me} />
+ A little context, right when you need it.
+
+ );
+}
+
+export function ModeSwitchExample({
+ density = "default",
+}: {
+ density?: "default" | "compact";
+}) {
+ const [mode, setMode] = useState<"chat" | "do" | "plan">("do");
+ return ;
+}
+
+export function SendControlExample({
+ density = "default",
+}: {
+ density?: "default" | "compact";
+}) {
+ const [running, setRunning] = useState(false);
+ return (
+
+ setRunning(true)}
+ onStop={() => setRunning(false)}
+ />
+
+ {running ? "Demo running — press stop" : "Ready to try"}
+
+
+ );
+}
+
+export function CompletionPaletteExample() {
+ const [selected, setSelected] = useState("Pick a slash command");
+ return (
+
+ Open slash commands}
+ commands={[
+ { id: "plan", label: "/plan", description: "Think before acting" },
+ {
+ id: "handoff",
+ label: "/handoff",
+ description: "Prepare a continuation",
+ },
+ ]}
+ onSelect={(command) => setSelected(command.label)}
+ />
+ {selected}
+
+ );
+}
+
+export function AttachmentChipExample() {
+ const [attached, setAttached] = useState(true);
+ return (
+
+ {attached ? (
+
setAttached(false)}
+ />
+ ) : (
+ setAttached(true)}>Restore attachment
+ )}
+
+
+ );
+}
+
+export function SearchFieldExample() {
+ const [query, setQuery] = useState("");
+ return (
+
+
setQuery(event.target.value)}
+ placeholder="Search your workspace…"
+ />
+
+ {query
+ ? `Searching for “${query}” in this demo`
+ : "Start typing to try the search field."}
+
+
+ );
+}
+
+export function ActivityItemExample() {
+ return (
+
+ );
+}
+
+export function BudgetPillExample() {
+ return (
+
+
+
+
+
+ );
+}
+
+export function ContextMeterExample() {
+ return (
+
+
+
+
+ );
+}
+
+export function EmptyStateExample() {
+ return (
+
+ );
+}
+
+export function ErrorStateExample() {
+ const [retry, setRetry] = useState(false);
+ return retry ? (
+ Demo retry received. No network request was made.
+ ) : (
+ setRetry(true)}>Try again}
+ />
+ );
+}
+
+export function FailureSurfaceExample() {
+ const [retried, setRetried] = useState(false);
+ return retried ? (
+ Demo retry received. No command was executed.
+ ) : (
+ setRetried(true) }]}
+ />
+ );
+}
+
+export function MetricDisplayExample() {
+ return (
+
+
+
+
+
+ );
+}
+
+export function PlanRowExample() {
+ return (
+
+ );
+}
+
+export function ResourceRowExample() {
+ return (
+
+
+
+
+ );
+}
+
+export function StatusIndicatorExample() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+export function ToolClassBadgeExample() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+export function ToolMixExample() {
+ return (
+
+ );
+}
+
+export function ComposerExample({
+ density = "default",
+}: {
+ density?: "default" | "compact";
+}) {
+ const [mode, setMode] = useState<"chat" | "do" | "plan">("do");
+ const [message, setMessage] = useState("Make something that feels like us.");
+ const [sent, setSent] = useState(false);
+ return (
+
+ {
+ setSent(true);
+ setMessage("");
+ }}
+ />
+
+ {sent
+ ? "Message received locally. This is an interactive UI demo."
+ : "Interactive demo · no model connected"}
+
+
+ );
+}
+
+export function RunRailExample({
+ density = "default",
+}: {
+ density?: "default" | "compact";
+}) {
+ return (
+
+ );
+}
+
+export function SessionHeaderExample() {
+ return (
+
+ );
+}
+
+export function TranscriptTurnExample() {
+ return (
+
+
+ The small details are the interface. I kept the structure simple, made
+ every state explicit, and left the source in your hands.
+
+
+ );
+}
diff --git a/apps/specimens/src/home.tsx b/apps/specimens/src/home.tsx
new file mode 100644
index 0000000..2b28924
--- /dev/null
+++ b/apps/specimens/src/home.tsx
@@ -0,0 +1,292 @@
+import { useState } from "react";
+import {
+ Asterisk,
+ ArrowDown,
+ ArrowRight,
+ ArrowUpRight,
+ Boxes,
+ Braces,
+ Check,
+ Code2,
+ GitBranch,
+ Layers,
+ RotateCcw,
+ Sparkles,
+} from "lucide-react";
+import {
+ Badge,
+ Button,
+ Composer,
+ PlanRow,
+ ResourceRow,
+ Separator,
+ TranscriptTurn,
+ buttonVariants,
+ cn,
+} from "@opencoven/ui";
+import { catalog, componentHref, installCommand } from "./catalog";
+import { CodeBlock } from "./code-block";
+
+function AgentShowcase() {
+ const [mode, setMode] = useState<"chat" | "do" | "plan">("do");
+ const [message, setMessage] = useState(
+ "Build a little less. Make it mean a little more.",
+ );
+ const [sent, setSent] = useState(false);
+ const [attached, setAttached] = useState(true);
+ return (
+
+
+
+ Your next
+ great interface
+
+
Interactive demo
+
+
+
+
+ feat / something-good{" "}
+ OpenCoven
+
+
+
+ {sent
+ ? "Got it. Your intent is clear. In your app, this is where your agent takes over."
+ : "Good interfaces make complex things feel simple. Let’s start with the parts that matter."}
+
+
+
+
+ setAttached(false)}
+ onSend={() => {
+ setSent(true);
+ setMessage("");
+ }}
+ />
+
+
+
+ {sent
+ ? "Received locally. No model connected."
+ : "Your intent. Your model. Your interface."}
+
+ {
+ setSent(false);
+ setAttached(true);
+ setMessage("Build a little less. Make it mean a little more.");
+ }}
+ >
+
+
+
+
+
+ Behind the interface
+
+ Nothing hidden.
+
+ Everything yours.
+
+
+ Readable source, thoughtful defaults, and room for your own point of
+ view.
+
+
+
+ Made of small things
+
+
+
+
+
+
+
+ Real components.
+
+ Not a screenshot.
+
+
+
+
+
+ );
+}
+
+export function Home() {
+ const featured = [
+ "mode-switch",
+ "plan-row",
+ "attachment-chip",
+ "context-meter",
+ "resource-row",
+ "status-indicator",
+ ].map((id) => catalog.find((entry) => entry.name === id)!);
+ return (
+
+
+
+ An open source beginning{" "}
+ Meet Coven UI{" "}
+
+
+
+ Built for agents.
+
+ Made for humans.
+
+
+ Thoughtful components for the next generation of interfaces.
+ Accessible. Composable. A little more
+ familiar.
+
+
+
+
+
+
+
+ Open source
+
+
+ Built on Base UI
+
+
+ shadcn compatible
+
+
+
+
+
+
A familiar feeling. A different kind of UI.
+
+ Go on, try it
+
+
+
+
+
+
+
+
+
Small pieces. Real possibilities.
+
A considered collection.
+
+ From the first prompt to the final detail. Make every state feel
+ right.
+
+
+
+ All {catalog.length} components
+
+
+
+
+
+
+
+
Compose, don’t compromise.
+
+ Small, focused primitives that work beautifully together. Start with
+ one. Build your own system.
+
+
+
+
+
Your code. Your call.
+
+ Copy the source into your project. Change the details, own the
+ behavior, and skip the black box.
+
+
+
+
+
Familiar, by design.
+
+ Clear intent, visible progress, thoughtful defaults. Interfaces that
+ keep people in the loop.
+
+
+
+
+
+ );
+}
diff --git a/apps/specimens/src/lab.tsx b/apps/specimens/src/lab.tsx
new file mode 100644
index 0000000..8fc6c38
--- /dev/null
+++ b/apps/specimens/src/lab.tsx
@@ -0,0 +1,186 @@
+import { useState } from "react";
+import {
+ Badge,
+ Button,
+ Card,
+ CardContent,
+ CardFooter,
+ CardHeader,
+ ResourceRow,
+ SessionHeader,
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+ TranscriptTurn,
+} from "@opencoven/ui";
+import { ComposerExample } from "./examples";
+import type { Density } from "./catalog";
+
+export function Lab({ density }: { density: Density }) {
+ const [notice, setNotice] = useState("");
+ return (
+
+
+
+
+
+
+
+ {["Composer", "Messages", "Context", "Actions", "Cards"].map(
+ (name) => (
+
+ {name}
+
+ ),
+ )}
+
+
+
+
+
+
+ The component source, registry item, and specimen share one
+ implementation boundary. Ready when you are.
+
+
+
+
+
+
+
+
+
+ Model selection, linked context, and send readiness stay
+ visible without interrupting the writing flow.
+
+
+
+
+ The same primitives carry a different familiar identity
+ without changing their accessibility contract.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Give your next step a little context.
+
+
+ setNotice("Demo: changed files attached as context.")
+ }
+ >
+ Attach changed files
+
+
+ setNotice(
+ "Demo: clarify the intent, keep the scope, and verify the result.",
+ )
+ }
+ >
+ Enhance prompt
+
+
+
+ {notice || "Local interaction examples. No files are accessed."}
+
+
+
+
+
+ {[
+ [
+ "Pull request",
+ "Recover attachment ingestion",
+ "Checks 12 / 12",
+ ],
+ ["Proposal", "Merge with confidence", "Awaiting your review"],
+ [
+ "Attachment",
+ "Components-preview.png",
+ "384 KB · added by Cody",
+ ],
+ ["Handoff", "Deployment ledger", "7 sections"],
+ ].map(([kind, title, meta]) => (
+
+
+ {kind}
+ {title}
+ {meta}
+
+
+ Example {kind?.toLowerCase()} surface composed from public
+ modules.
+
+
+
+ setNotice(
+ `${kind}: ${title}. This is sample content in the component lab.`,
+ )
+ }
+ >
+ Inspect example
+
+
+
+ ))}
+
{notice}
+
+
+
+
+
+ Example data only. These components connect to your own application
+ logic.
+
+
+ );
+}
diff --git a/apps/specimens/src/main.tsx b/apps/specimens/src/main.tsx
index ba2fdcb..90b76dd 100644
--- a/apps/specimens/src/main.tsx
+++ b/apps/specimens/src/main.tsx
@@ -6,20 +6,6 @@ import "./specimens.css";
import "./specimens-fixes.css";
import { App } from "./app";
-const normalizedPath = window.location.pathname.replace(/\/+$/, "") || "/";
-
-if (normalizedPath !== "/") {
- window.addEventListener(
- "keydown",
- (event) => {
- if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
- event.stopImmediatePropagation();
- }
- },
- { capture: true },
- );
-}
-
const root = document.getElementById("root");
if (!root) {
diff --git a/apps/specimens/src/specimens-fixes.css b/apps/specimens/src/specimens-fixes.css
index 4894d88..2116a3f 100644
--- a/apps/specimens/src/specimens-fixes.css
+++ b/apps/specimens/src/specimens-fixes.css
@@ -5,316 +5,37 @@ html {
min-width: 320px;
}
-body:not(:has(#group-composer)) .specimen-rail a[href="#group-composer"],
-body:not(:has(#group-run-rail)) .specimen-rail a[href="#group-run-rail"],
-body:not(:has(#group-blocks)) .specimen-rail a[href="#group-blocks"] {
- display: none;
+/* Nested source previews must scroll themselves rather than widen the page. */
+.docs-main,
+.component-stage,
+.showcase-conversation,
+.lab-stage,
+.home-component,
+.doc-preview,
+.doc-preview [data-slot="tabs"],
+.doc-preview [data-slot="tabs-content"] {
+ min-width: 0;
+ max-width: 100%;
}
-@media (max-width: 68rem) {
- .specimen-topbar__inner {
- display: flex;
- flex-wrap: wrap;
- }
-
- .surface-switcher {
- margin-inline-start: auto;
- }
-
- .specimen-topbar__actions {
- display: flex;
- flex: 1 0 100%;
- flex-wrap: wrap;
- }
-
- .specimen-search {
- width: auto;
- max-width: none;
- min-width: 7rem;
- flex: 1 1 10rem;
- }
-
- .specimen-shell {
- grid-template-columns: minmax(0, 1fr);
- }
+.component-stage > *,
+.home-component-preview > *,
+.lab-stage > * {
+ min-width: 0;
+ max-width: 100%;
}
@media (max-width: 48rem) {
- .specimen-shell {
- box-sizing: border-box;
- width: 100%;
- min-width: 0;
- max-width: 100vw;
- margin-inline: 0;
- }
-
- .specimen-main__inner {
- box-sizing: border-box;
- width: 100%;
- min-width: 0;
- max-width: 100%;
- }
-
- .specimen-hero,
- .specimen-hero__copy,
- .specimen-stats {
- min-width: 0;
- max-width: 100%;
- }
-
- .specimen-stats {
- grid-template-columns: repeat(3, minmax(0, 1fr));
- }
-
- .specimen-stats div {
- min-width: 0;
- }
-
- .specimen-stats :where(dt, dd) {
- overflow-wrap: anywhere;
- }
-
- .specimen-rail {
- box-sizing: border-box;
- width: 100%;
- min-width: 0;
- max-width: 100%;
- }
-
- .specimen-rail > *,
- .specimen-rail__context,
- .specimen-rail__nav,
- .specimen-rail__package {
- box-sizing: border-box;
- min-width: 0;
- max-width: 100%;
+ .component-stage [data-slot="session-header"],
+ .lab-surface [data-slot="session-header"] {
+ flex-wrap: wrap;
}
-
- .specimen-rail__context,
- .specimen-rail__package,
- .specimen-rail__package code {
+ .component-stage [data-slot="resource-row"],
+ .lab-stage [data-slot="resource-row"] {
overflow-wrap: anywhere;
- word-break: break-word;
- }
-
- .specimen-rail__nav {
- display: grid;
- width: 100%;
- min-width: 0;
- grid-template-columns: repeat(auto-fit, minmax(min(100%, 5.5rem), 1fr));
- }
-
- .specimen-rail__nav:has(a:only-child) {
- grid-template-columns: minmax(0, 1fr);
}
-
- .specimen-rail__nav a {
- min-width: 0;
- justify-content: center;
- gap: 0.375rem;
- padding-inline: 0.375rem;
- }
-
- .specimen-grid {
- grid-template-columns: minmax(0, 1fr);
- }
-
- .specimen-card {
- box-sizing: border-box;
- width: 100%;
- min-width: 0;
- max-width: 100%;
- }
-
- .specimen-card > [data-slot="tabs"] {
- width: 100%;
- min-width: 0;
+ .code-block pre {
max-width: 100%;
- gap: 0;
- }
-
- .specimen-card > [data-slot="tabs"] > [data-slot="tabs-list"] {
- display: grid;
- box-sizing: border-box;
- width: 100% !important;
- min-width: 0;
- max-width: 100%;
- min-height: 2.75rem;
- grid-template-columns: repeat(3, minmax(5.25rem, 1fr));
- gap: 0;
- margin: 0 !important;
overflow-x: auto;
- border-block-end: 1px solid var(--border);
- padding: 0 0.75rem;
- scrollbar-width: thin;
- }
-
- .specimen-card
- > [data-slot="tabs"]
- > [data-slot="tabs-list"]
- > [data-slot="tabs-trigger"] {
- min-width: 0;
- max-width: 100%;
- min-height: 2.75rem;
- padding: 0.625rem 0.5rem;
- }
-
- .specimen-card
- > [data-slot="tabs"]
- > [data-slot="tabs-list"]
- > [data-slot="tabs-trigger"][data-active]::after {
- inset-block-end: 0 !important;
- }
-
- .specimen-card
- > [data-slot="tabs"]
- > [data-slot="tabs-list"]
- > [data-slot="tabs-trigger"]:focus-visible {
- outline-offset: -3px;
- }
-
- .specimen-card > [data-slot="tabs"] > [data-slot="tabs-content"] {
- width: 100%;
- min-width: 0;
- max-width: 100%;
- }
-
- .specimen-card__header {
- min-height: 0;
- padding: 1rem;
- }
-
- .specimen-card__title {
- margin-block-start: 0.875rem;
- }
-
- .specimen-card__description {
- margin-block-start: 0.375rem;
- line-height: 1.5;
- }
-
- .specimen-stage {
- box-sizing: border-box;
- width: 100%;
- min-width: 0;
- min-height: 0;
- max-width: 100%;
- align-content: start;
- justify-items: stretch;
- border-block-start: 0;
- padding: 1rem;
- }
-
- .specimen-stage > * {
- box-sizing: border-box;
- width: 100%;
- min-width: 0;
- max-width: 100%;
- }
-
- .specimen-stage *,
- .specimen-documentation * {
- min-width: 0;
- max-width: 100%;
- }
-
- .specimen-stage :where(p, span, strong, small, code),
- .specimen-documentation :where(p, span, strong, small, code) {
- overflow-wrap: anywhere;
- }
-
- .specimen-documentation {
- box-sizing: border-box;
- width: 100%;
- min-width: 0;
- min-height: 0;
- max-width: 100%;
- padding: 1rem;
- }
-
- .specimen-command {
- max-width: 100%;
- }
-}
-
-@media (max-width: 24.375rem) {
- .assembled-lab > [data-slot="tabs"],
- .assembled-lab [data-slot="tabs-content"],
- .assembled-lab__nav,
- .assembled-lab__stage {
- box-sizing: border-box;
- width: 100%;
- min-width: 0;
- max-width: 100%;
- }
-
- .assembled-lab__nav {
- overflow-x: hidden;
- }
-
- .assembled-lab__tabs {
- display: grid;
- box-sizing: border-box;
- width: 100% !important;
- min-width: 0;
- max-width: 100%;
- grid-template-columns: repeat(5, minmax(0, 1fr));
- }
-
- .assembled-lab__tabs [role="tab"] {
- min-width: 0;
- padding-inline: 0.25rem;
- font-size: 0.625rem;
- white-space: normal;
- }
-
- .assembled-lab__stage {
- min-height: 0;
- align-content: start;
- }
-
- .assembled-lab__stage > * {
- min-width: 0;
- max-width: 100%;
- }
-}
-
-@media (max-width: 20rem) {
- .specimen-topbar__inner {
- gap: 0.375rem;
- padding-block: 0.375rem;
- }
-
- .specimen-main__inner {
- padding-block-start: 0.75rem;
- }
-
- .specimen-hero {
- gap: 0.75rem;
- margin-block-end: 1.25rem;
- padding-block-end: 0.75rem;
- }
-
- .specimen-hero h1 {
- font-size: 2rem;
- }
-
- .specimen-hero__copy > p:last-child {
- margin-block-start: 0.625rem;
- }
-
- .catalog-group__header {
- gap: 0.375rem;
- }
-
- .catalog-group__count {
- display: none;
- }
-}
-
-@media (prefers-reduced-motion: reduce) {
- .specimen-card:hover,
- .specimen-card:focus-within {
- translate: none;
}
}
diff --git a/apps/specimens/src/specimens.css b/apps/specimens/src/specimens.css
index 19353e6..7dd3ec5 100644
--- a/apps/specimens/src/specimens.css
+++ b/apps/specimens/src/specimens.css
@@ -1,912 +1,1470 @@
+:root {
+ --background: #f7f6f9;
+ --foreground: #242329;
+ --card: #ffffff;
+ --card-foreground: var(--foreground);
+ --muted-foreground: #716c7b;
+ --presence: #75629e;
+ --presence-foreground: var(--card);
+ --border: color-mix(in srgb, var(--foreground) 12%, var(--background));
+ --font-numeric: var(--font-ui);
+ --site-width: 1240px;
+}
+html.dark {
+ --background: #111113;
+ --foreground: #efedf2;
+ --card: #19191c;
+ --card-foreground: var(--foreground);
+ --muted-foreground: #99979f;
+ --presence: #baabe6;
+ --presence-foreground: var(--background);
+ --border: color-mix(in srgb, var(--foreground) 12%, var(--background));
+}
html {
min-width: 320px;
- --specimen-topbar-height: 3.75rem;
- scroll-padding-top: calc(var(--specimen-topbar-height) + 1rem);
+ scroll-padding-top: 100px;
}
-
body {
min-height: 100vh;
- overflow-x: clip;
}
-
+.site {
+ font-size: 14px;
+ line-height: 1.5;
+}
+.site a {
+ text-decoration: none;
+}
+.site button:not(:disabled),
+.site select {
+ cursor: pointer;
+}
+.site :where(a, button, input, select, textarea):focus-visible {
+ outline: 2px solid var(--presence);
+ outline-offset: 4px;
+}
+.site p {
+ text-wrap: pretty;
+}
+.site h1,
+.site h2,
+.site h3 {
+ text-wrap: balance;
+}
+.site em {
+ font-weight: 400;
+}
.skip-link {
position: fixed;
- inset-block-start: 0.75rem;
- inset-inline-start: 0.75rem;
+ top: 12px;
+ left: 12px;
z-index: 100;
- translate: 0 -200%;
- border-radius: var(--radius-2);
+ transform: translateY(-180%);
background: var(--foreground);
color: var(--background);
- padding: 0.625rem 0.875rem;
- font-size: 0.8125rem;
- font-weight: 700;
+ padding: 12px 20px;
+ border-radius: 6px;
}
-
.skip-link:focus {
- translate: 0;
+ transform: translateY(0);
}
-
-.specimen-topbar {
+.site-header {
position: sticky;
- inset-block-start: 0;
- z-index: 50;
- border-block-end: 1px solid var(--border);
- background: color-mix(in srgb, var(--background) 92%, transparent);
- backdrop-filter: blur(18px);
-}
-
-.specimen-topbar__inner {
+ top: 0;
+ z-index: 40;
+ background: var(--background);
+ border-bottom: 1px solid var(--border);
+}
+.site-header-inner {
+ max-width: 1440px;
+ min-height: 76px;
+ margin-inline: auto;
+ padding-inline: 40px;
display: flex;
- flex-wrap: wrap;
- min-height: 3.75rem;
- max-width: 96rem;
align-items: center;
- gap: 0.75rem;
- margin-inline: auto;
- padding-inline: clamp(1rem, 3vw, 1.5rem);
+ gap: 52px;
}
-
-.specimen-brand {
+.site-brand {
display: inline-flex;
align-items: center;
- gap: 0.75rem;
- min-width: max-content;
+ gap: 8px;
color: var(--foreground);
- text-decoration: none;
+ flex-shrink: 0;
}
-
-.specimen-brand__mark {
- display: grid;
- width: 2rem;
- height: 2rem;
- flex: none;
- place-items: center;
- border: 1px solid color-mix(in srgb, var(--presence) 42%, var(--border));
- border-radius: var(--radius-2);
- background: color-mix(in srgb, var(--presence) 12%, var(--card));
+.site-brand > svg {
+ width: 34px;
+ height: 34px;
color: var(--presence);
+ stroke-width: 2;
}
-
-.specimen-brand__mark svg {
- width: 1rem;
- height: 1rem;
+.site-brand > span {
+ font-size: 25px;
+ font-weight: 650;
+ letter-spacing: -1.3px;
}
-
-.specimen-brand > span:last-child {
- display: grid;
- font-size: 0.875rem;
- font-weight: 750;
- letter-spacing: -0.015em;
- line-height: 1.1;
+.brand-ui {
+ color: var(--muted-foreground);
+ font-size: 17px;
+ letter-spacing: -0.7px;
+ margin-left: 4px;
+ font-weight: 450;
}
-
-.specimen-brand small {
- margin-block-start: 0.2rem;
+.primary-nav {
+ display: flex;
+ align-items: center;
+ gap: 28px;
+}
+.primary-nav a {
color: var(--muted-foreground);
- font-family: var(--font-numeric);
- font-size: 0.625rem;
- font-weight: 600;
- letter-spacing: 0.08em;
- text-transform: uppercase;
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+}
+.primary-nav a:hover,
+.primary-nav a[aria-current],
+.site-footer a:hover {
+ color: var(--foreground);
+}
+.nav-dot,
+.release-dot {
+ width: 5px;
+ height: 5px;
+ border-radius: 50%;
+ background: var(--presence);
+}
+.header-tools {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ margin-left: auto;
}
-
-.surface-switcher,
-.density-control {
+.icon-link {
display: inline-flex;
align-items: center;
- gap: 0.125rem;
- padding: 0.1875rem;
+ color: var(--muted-foreground);
+ padding: 6px;
+}
+.icon-link svg {
+ width: 18px;
+ height: 18px;
+}
+.site-search {
+ position: relative;
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ width: 212px;
+ height: 34px;
border: 1px solid var(--border);
- border-radius: var(--radius-2);
+ border-radius: 6px;
background: var(--card);
+ color: var(--muted-foreground);
+ padding-inline: 10px;
}
-
-.surface-switcher a,
-.density-control button {
- border: 0;
- border-radius: calc(var(--radius-2) - 2px);
+.site-search > svg {
+ width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+}
+.site-search input {
+ min-width: 0;
+ width: 100%;
background: transparent;
+ border: none;
+ color: var(--foreground);
+ outline: none;
+ font-size: 14px;
+}
+.site-search input::placeholder {
color: var(--muted-foreground);
- padding: 0.375rem 0.75rem;
- font-size: 0.75rem;
- font-weight: 700;
- line-height: 1.25rem;
- text-decoration: none;
+}
+.site-search:focus-within {
+ outline: 2px solid var(--presence);
+ outline-offset: 2px;
+}
+.site-search input:focus-visible {
+ outline: none;
+}
+.site-search kbd {
white-space: nowrap;
+ font: inherit;
+ font-size: 14px;
}
-
-.surface-switcher a:hover,
-.density-control button:hover {
- background: var(--muted);
+.search-results {
+ position: absolute;
+ right: 0;
+ top: 44px;
+ width: 320px;
+ max-height: 440px;
+ overflow-y: auto;
+ padding: 8px;
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ background: var(--card);
color: var(--foreground);
+ box-shadow: var(--elevation-2);
}
-
-.surface-switcher a[aria-current="page"],
-.density-control button[aria-pressed="true"] {
- background: var(--foreground);
- color: var(--background);
- box-shadow: var(--elevation-1);
+.search-heading {
+ display: block;
+ color: var(--muted-foreground);
+ padding: 8px;
}
-
-.specimen-topbar__actions {
+.search-result {
display: flex;
- flex: 1 1 34rem;
- flex-wrap: wrap;
- min-width: 0;
+ justify-content: space-between;
align-items: center;
- justify-content: flex-end;
- gap: 0.5rem;
+ padding: 10px;
+ border-radius: 5px;
}
-
-.specimen-search {
- width: min(20rem, 34vw);
- max-width: 20rem;
- flex: 1 1 16rem;
+.search-result:hover,
+.search-result:focus-visible {
+ background: var(--muted);
}
-
-.scheme-control {
- flex: none;
- white-space: nowrap;
+.search-result svg {
+ width: 14px;
+ height: 14px;
}
-
-.scheme-control svg {
- width: 0.875rem;
- height: 0.875rem;
+.search-results > p {
+ padding: 12px;
}
-
-.specimen-shell {
- display: grid;
- max-width: 96rem;
- min-height: calc(100vh - var(--specimen-topbar-height));
- grid-template-columns: 13.5rem minmax(0, 1fr);
+.mobile-nav-button {
+ display: none;
+}
+.home-main {
+ max-width: var(--site-width);
margin-inline: auto;
+ padding-inline: 40px;
}
-
-.specimen-rail {
- position: sticky;
- inset-block-start: var(--specimen-topbar-height);
- align-self: start;
- height: calc(100vh - var(--specimen-topbar-height));
- overflow-y: auto;
- border-inline-end: 1px solid var(--border);
- background: color-mix(in srgb, var(--card) 72%, var(--background));
- padding: 1.5rem 0.875rem;
- scrollbar-width: thin;
+.home-hero {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ padding-block: 78px 60px;
+}
+.release-note {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ color: var(--muted-foreground);
+ font-size: 14px;
}
-
-.specimen-rail__context {
- padding-inline: 0.625rem;
+.release-note svg {
+ width: 14px;
+ height: 14px;
}
-
-.specimen-rail__context h2 {
- margin: 0.4rem 0 0;
- font-size: 1rem;
- font-weight: 750;
- letter-spacing: -0.015em;
+.release-divider {
+ height: 13px;
+ width: 1px;
+ background: var(--border);
}
-
-.specimen-rail__context p:last-child {
- margin: 0.625rem 0 0;
- color: var(--muted-foreground);
- font-size: 0.75rem;
- line-height: 1.55;
+.home-hero h1 {
+ font-size: clamp(56px, 7.6vw, 96px);
+ font-weight: 450;
+ letter-spacing: -5px;
+ line-height: 1.07;
+ margin: 30px 0 24px;
}
-
-.specimen-kicker {
+.home-hero h1 em {
+ color: var(--presence);
+ letter-spacing: -4px;
+}
+.hero-description {
+ color: var(--muted-foreground);
+ font-size: 17px;
+ line-height: 1.7;
margin: 0;
+ max-width: 620px;
+}
+.hero-actions {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding-top: 29px;
+}
+.hero-cta {
+ height: 42px;
+ padding-inline: 20px;
+ border-radius: 7px;
+ font-size: 14px;
+}
+.hero-install {
+ padding-top: 22px;
+ max-width: 100%;
+}
+.hero-foundations {
+ display: flex;
+ gap: 22px;
+ align-items: center;
+ color: var(--muted-foreground);
+ padding-top: 18px;
+ font-size: 14px;
+}
+.hero-foundations span {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+}
+.hero-foundations svg {
+ width: 13px;
+ height: 13px;
color: var(--presence);
- font-size: 0.625rem;
- font-weight: 750;
- letter-spacing: 0.14em;
- text-transform: uppercase;
}
-
-.specimen-rail__nav {
- display: grid;
- gap: 0.25rem;
- margin-block-start: 1.25rem;
+.code-block {
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ background: var(--card);
+ color: var(--foreground);
+ overflow: hidden;
+ max-width: 100%;
+ min-width: 0;
+ text-align: left;
}
-
-.specimen-rail__nav a {
+.code-block__bar {
display: flex;
align-items: center;
justify-content: space-between;
- gap: 0.75rem;
- border-radius: var(--radius-2);
+ padding: 8px 12px;
+ border-bottom: 1px solid var(--border);
color: var(--muted-foreground);
- padding: 0.5rem 0.625rem;
- font-size: 0.8125rem;
- font-weight: 650;
- text-decoration: none;
}
-
-.specimen-rail__nav a:hover {
- background: var(--muted);
- color: var(--foreground);
+.code-block__bar > span {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
}
-
-.specimen-rail__nav small {
- color: color-mix(in srgb, var(--muted-foreground) 76%, transparent);
- font-size: 0.625rem;
+.code-block__bar svg {
+ width: 14px;
+ height: 14px;
}
-
-.specimen-rail__package {
- margin-block-start: 1.5rem;
- border-block-start: 1px solid var(--border);
- padding: 1rem 0.625rem 0;
+.code-block pre {
+ margin: 0;
+ padding: 20px;
+ overflow-x: auto;
+ font-size: 14px;
+ line-height: 1.7;
+ tab-size: 2;
}
-
-.specimen-rail__package code {
- display: block;
- overflow: hidden;
- margin-block-start: 0.5rem;
- color: var(--foreground);
- font-size: 0.75rem;
- text-overflow: ellipsis;
- white-space: nowrap;
+.code-block code {
+ white-space: pre;
}
-
-.specimen-rail__package p {
- margin: 0.625rem 0 0;
+.code-block--compact {
+ display: flex;
+ align-items: center;
+ flex-direction: row-reverse;
+ background: transparent;
+ border-color: transparent;
+}
+.code-block--compact .code-block__bar {
+ padding: 0;
+ border: none;
+}
+.code-block--compact .code-block__bar > span {
+ display: none;
+}
+.code-block--compact pre {
+ padding: 4px 8px;
color: var(--muted-foreground);
- font-size: 0.7rem;
- line-height: 1.5;
+ font-size: 14px;
}
-
-.specimen-main {
- min-width: 0;
+.copy-feedback {
+ display: block;
+ padding: 8px 12px;
+ color: var(--presence);
}
-
-.specimen-main__inner {
- width: min(100%, 82.5rem);
- margin-inline: auto;
- padding: clamp(1.5rem, 3vw, 2.75rem) clamp(1rem, 3vw, 2.5rem) 5rem;
+.section-overline {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ color: var(--muted-foreground);
+ font-size: 14px;
+ padding-bottom: 15px;
}
-
-.specimen-hero {
- display: grid;
- grid-template-columns: minmax(0, 1fr) minmax(15rem, 20rem);
+.section-overline > span:last-child {
+ display: flex;
+ gap: 8px;
align-items: center;
- gap: 1.5rem;
- margin-block-end: clamp(2rem, 4vw, 3rem);
- border-block-end: 1px solid var(--border);
- padding-block-end: clamp(1.5rem, 3vw, 2rem);
}
-
-.specimen-hero__copy {
- max-width: 52rem;
+.section-overline svg {
+ width: 14px;
+ height: 14px;
}
-
-.specimen-hero h1 {
- max-width: 19ch;
- margin: 0.5rem 0 0;
- font-family: var(--font-editorial);
- font-size: clamp(2.4rem, 4.6vw, 4.25rem);
+.agent-showcase {
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ background: var(--card);
+ overflow: hidden;
+ box-shadow: 0 12px 48px color-mix(in srgb, var(--background) 80%, transparent);
+}
+.showcase-title {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 17px 24px;
+ border-bottom: 1px solid var(--border);
+}
+.showcase-title > span:first-child {
+ display: inline-flex;
+ align-items: center;
+ gap: 9px;
font-weight: 500;
- letter-spacing: -0.04em;
- line-height: 1;
- color: var(--foreground);
- text-wrap: balance;
}
-
-.specimen-hero__copy > p:last-child {
- max-width: 68ch;
- margin: 0.875rem 0 0;
- color: var(--muted-foreground);
- font-size: 0.875rem;
- line-height: 1.6;
+.coven-glyph {
+ width: 20px;
+ height: 20px;
+ color: var(--presence);
}
-
-.specimen-stats {
+.showcase-workspace {
display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 0.5rem;
- margin: 0;
+ grid-template-columns: minmax(0, 1fr) 270px;
}
-
-.specimen-stats div {
+.showcase-conversation {
min-width: 0;
- border-inline-start: 1px solid var(--border);
- padding-inline-start: 0.875rem;
+ padding: 24px 28px 12px;
}
-
-.specimen-stats dt {
+.showcase-context {
+ display: flex;
+ align-items: center;
+ gap: 7px;
color: var(--muted-foreground);
- font-size: 0.625rem;
- font-weight: 700;
- letter-spacing: 0.08em;
- text-transform: uppercase;
+ font-size: 14px;
+ padding-bottom: 28px;
}
-
-.specimen-stats dd {
- margin: 0.25rem 0 0;
- color: var(--foreground);
- font-size: 1.25rem;
- font-weight: 650;
+.showcase-context svg {
+ width: 14px;
+ height: 14px;
}
-
-.catalog {
- display: grid;
- gap: clamp(2.75rem, 5vw, 4.5rem);
+.showcase-context span {
+ margin-left: auto;
}
-
-.catalog-group {
- scroll-margin-block-start: 0;
+.showcase-plan {
+ padding-block: 16px 24px;
}
-
-.catalog-group__header {
+.showcase-composer {
+ min-width: 0;
+}
+.showcase-footnote {
display: flex;
- align-items: end;
+ align-items: center;
justify-content: space-between;
- gap: 2rem;
- margin-block-end: 0.875rem;
+ color: var(--muted-foreground);
+ font-size: 14px;
+ padding-top: 8px;
}
-
-.catalog-group__eyebrow {
- margin: 0;
+.showcase-evidence {
+ border-left: 1px solid var(--border);
+ display: flex;
+ flex-direction: column;
+ padding: 28px 24px;
+ background: color-mix(in srgb, var(--card) 55%, var(--background));
+}
+.eyebrow {
+ color: var(--muted-foreground);
+ font-size: 14px;
+ letter-spacing: 0.01em;
+}
+.showcase-evidence h3 {
+ font-family: var(--font-editorial);
+ font-size: 26px;
+ font-weight: 400;
+ line-height: 1.35;
+ margin: 16px 0 30px;
+}
+.evidence-ownership {
+ font-size: 14px;
+ line-height: 1.7;
+ color: var(--muted-foreground);
+ padding-bottom: 24px;
+}
+.evidence-files {
+ padding-top: 24px;
+}
+.evidence-files > span {
+ display: block;
+ color: var(--muted-foreground);
+ padding-bottom: 12px;
+}
+.evidence-note {
+ margin-top: auto;
+ padding-top: 32px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ color: var(--muted-foreground);
+}
+.evidence-note svg {
+ width: 25px;
+ height: 25px;
color: var(--presence);
- font-size: 0.625rem;
- font-weight: 750;
- letter-spacing: 0.14em;
- text-transform: uppercase;
-}
-
-.catalog-group__header h2 {
- max-width: 34rem;
- margin: 0.35rem 0 0;
- font-size: clamp(1.25rem, 2vw, 1.6rem);
- font-weight: 700;
- letter-spacing: -0.025em;
- line-height: 1.2;
- color: var(--foreground);
- text-wrap: balance;
}
-
-.catalog-group__summary {
- max-width: 52ch;
- margin: 0.35rem 0 0;
+.evidence-note p {
+ font-size: 14px;
+ line-height: 1.6;
+}
+.showcase-caption {
+ display: flex;
+ justify-content: space-between;
+ padding-top: 16px;
+ font-size: 14px;
color: var(--muted-foreground);
- font-size: 0.75rem;
- line-height: 1.5;
}
-
-.catalog-group__count {
- flex: none;
+.showcase-caption a,
+.section-heading > a {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+}
+.showcase-caption svg,
+.section-heading > a svg {
+ width: 16px;
+ height: 16px;
+}
+.home-library {
+ padding-top: 106px;
+}
+.section-heading {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+ padding-bottom: 28px;
+}
+.section-heading h2 {
+ font-size: 36px;
+ letter-spacing: -1.3px;
+ font-weight: 450;
+ margin: 12px 0;
+}
+.section-heading p:last-child {
color: var(--muted-foreground);
- font-size: 0.6875rem;
- letter-spacing: 0.04em;
- text-transform: uppercase;
+ margin: 0;
+}
+.section-heading > a {
+ flex-shrink: 0;
+ font-size: 14px;
+ padding-bottom: 4px;
}
-
-.specimen-grid {
+.home-component-grid {
display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 0.75rem;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 16px;
}
-
-.specimen-card {
- min-width: 0;
- overflow: hidden;
- scroll-margin-block-start: 0;
+.home-component {
border: 1px solid var(--border);
- border-radius: var(--radius-3);
+ border-radius: 9px;
background: var(--card);
- color: var(--card-foreground);
- box-shadow: var(--elevation-1);
- transition:
- border-color var(--motion-standard) var(--motion-ease),
- box-shadow var(--motion-standard) var(--motion-ease),
- translate var(--motion-standard) var(--motion-ease);
-}
-
-.specimen-card:hover,
-.specimen-card:focus-within {
- border-color: color-mix(in srgb, var(--presence) 34%, var(--border));
- box-shadow: var(--elevation-2);
- translate: 0 -1px;
+ min-width: 0;
+ overflow: hidden;
}
-
-.specimen-card__header {
- min-height: 8.25rem;
- border-block-end: 1px solid var(--border);
- padding: 1rem;
+.home-component-preview {
+ display: flex;
+ min-height: 195px;
+ justify-content: center;
+ align-items: center;
+ padding: 20px;
+ overflow: auto;
+}
+.home-component-preview [data-slot="plan-row"] {
+ font-size: 14px;
}
-
-.specimen-card__meta {
+.home-component > a {
display: flex;
align-items: center;
justify-content: space-between;
- gap: 1rem;
+ border-top: 1px solid var(--border);
+ padding: 15px 20px;
+ font-size: 14px;
+}
+.home-component > a:hover {
+ color: var(--presence);
+ background: var(--muted);
}
-
-.specimen-card__index {
+.home-component > a svg {
+ width: 16px;
+ height: 16px;
color: var(--muted-foreground);
- font-size: 0.625rem;
- font-weight: 700;
- letter-spacing: 0.12em;
}
-
-.specimen-card__title {
- margin: 0.75rem 0 0;
- font-size: 1.0625rem;
- font-weight: 750;
- letter-spacing: -0.015em;
+.principles {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 45px;
+ padding-block: 80px;
}
-
-.specimen-card__description {
- max-width: 46ch;
- margin: 0.45rem 0 0;
+.principles svg {
+ width: 22px;
+ height: 22px;
+ color: var(--presence);
+}
+.principles h3 {
+ font-size: 16px;
+ font-weight: 500;
+ margin: 16px 0 10px;
+}
+.principles p {
+ font-size: 14px;
color: var(--muted-foreground);
- font-size: 0.8125rem;
- line-height: 1.55;
+ line-height: 1.7;
+ margin: 0;
}
-
-.specimen-stage {
- display: grid;
- min-height: 12.5rem;
- align-content: center;
- justify-items: center;
- border-block-start: 1px solid
- color-mix(in srgb, var(--border) 62%, transparent);
- background: color-mix(in srgb, var(--muted) 48%, var(--background));
- padding: clamp(1rem, 2vw, 1.5rem);
-}
-
-.specimen-stage > * {
- max-width: 100%;
+.home-closing {
+ border-top: 1px solid var(--border);
+ text-align: center;
+ padding-block: 70px 90px;
}
-
-.specimen-stage > [data-slot="composer"],
-.specimen-stage > [data-slot="session-header"] {
- width: 100%;
+.home-closing h2 {
+ font-size: 55px;
+ line-height: 1.14;
+ letter-spacing: -2px;
+ font-weight: 450;
+ margin: 22px 0 32px;
}
-
-.specimen-stage > [data-slot="run-rail"] {
- width: min(100%, 28rem);
+.home-closing em {
+ color: var(--presence);
}
-
-.specimen-stage > [data-slot="transcript-turn"] {
- width: min(100%, 42rem);
+.site-footer {
+ max-width: 1440px;
+ padding: 28px 40px;
+ margin-inline: auto;
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ border-top: 1px solid var(--border);
}
-
-.specimen-documentation {
- display: grid;
- min-height: 12.5rem;
- align-content: start;
- gap: 0.875rem;
- padding: 1.25rem;
- font-size: 0.8125rem;
- line-height: 1.6;
+.site-footer .site-brand > span {
+ font-size: 21px;
}
-
-.specimen-documentation p {
- margin: 0;
+.site-footer .site-brand > svg {
+ width: 27px;
+ height: 27px;
}
-
-.specimen-install-grid {
- display: grid;
- gap: 0.75rem;
+.site-footer > p {
+ color: var(--muted-foreground);
+ font-size: 14px;
+}
+.site-footer nav {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+ margin-left: auto;
+ color: var(--muted-foreground);
+ font-size: 14px;
+}
+.site-footer nav a {
+ display: inline-flex;
+ gap: 5px;
+ align-items: center;
}
-
-.specimen-code-snippet {
+.site-footer nav svg {
+ width: 14px;
+ height: 14px;
+}
+.docs-layout {
display: grid;
- gap: 0.4rem;
+ grid-template-columns: 220px minmax(0, 1fr) 170px;
+ max-width: 1440px;
+ margin-inline: auto;
+ padding-inline: 40px;
+ gap: 48px;
+ min-height: 80vh;
}
-
-.specimen-code-snippet__header {
+.docs-sidebar {
+ position: sticky;
+ top: 76px;
+ align-self: start;
+ height: calc(100vh - 76px);
+ overflow-y: auto;
+ padding-block: 32px;
+ padding-right: 16px;
+ scrollbar-width: thin;
+ scrollbar-color: var(--border) transparent;
+}
+.sidebar-group {
display: flex;
- align-items: baseline;
+ flex-direction: column;
+ padding-bottom: 24px;
+}
+.sidebar-group h2 {
+ font-size: 14px;
+ font-weight: 600;
+ padding-inline: 10px;
+ margin: 0 0 8px;
+}
+.sidebar-group a {
+ padding: 7px 10px;
+ color: var(--muted-foreground);
+ border-radius: 5px;
+ display: flex;
+ align-items: center;
justify-content: space-between;
- gap: 0.75rem;
}
-
-.specimen-code-snippet__header span {
+.sidebar-group a:hover {
color: var(--foreground);
- font-size: 0.6875rem;
- font-weight: 750;
- letter-spacing: 0.08em;
- text-transform: uppercase;
}
-
-.specimen-code-snippet__header small {
- color: var(--muted-foreground);
- font-size: 0.625rem;
+.sidebar-group a[aria-current] {
+ color: var(--presence);
+ background: color-mix(in srgb, var(--presence) 9%, transparent);
+}
+.sidebar-group a span {
+ font-size: 14px;
+}
+.sidebar-lab {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ padding: 14px;
+}
+.sidebar-lab > svg {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
}
-
-.specimen-command {
+.sidebar-lab small {
display: block;
+ color: var(--muted-foreground);
+ font-size: 14px;
+ margin-top: 4px;
+}
+.docs-main {
+ min-width: 0;
+ padding-block: 36px 80px;
+}
+.breadcrumbs {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ color: var(--muted-foreground);
+ font-size: 14px;
+ margin: 0 0 32px;
+}
+.breadcrumbs a:hover {
+ color: var(--foreground);
+}
+.docs-heading {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+.docs-heading h1 {
+ font-size: clamp(36px, 4.1vw, 52px);
+ font-weight: 450;
+ letter-spacing: -2px;
+ line-height: 1.13;
+ margin: 20px 0 16px;
+}
+.docs-heading > p {
+ color: var(--muted-foreground);
+ font-size: 16px;
+ line-height: 1.65;
margin: 0;
- overflow-wrap: anywhere;
+ max-width: 640px;
+}
+.source-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ color: var(--muted-foreground);
+ font-size: 14px;
+}
+.docs-heading .source-link {
+ margin-top: 18px;
+}
+.source-link svg {
+ width: 14px;
+ height: 14px;
+}
+.doc-preview {
border: 1px solid var(--border);
- border-radius: var(--radius-2);
- background: var(--muted);
+ border-radius: 10px;
+ margin-top: 28px;
+ overflow: hidden;
+}
+.preview-toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 16px;
+ border-bottom: 1px solid var(--border);
+}
+.density-select {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ color: var(--muted-foreground);
+ font-size: 14px;
+}
+.density-select svg {
+ width: 14px;
+ height: 14px;
+}
+.density-select select {
+ background: var(--background);
color: var(--foreground);
- padding: 0.75rem;
- font-size: 0.7rem;
- line-height: 1.6;
- white-space: pre-wrap;
- word-break: break-word;
+ border-radius: 5px;
+ border: 1px solid var(--border);
+ padding: 4px;
+ font: inherit;
+}
+.component-stage {
+ min-height: 280px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 40px;
+ min-width: 0;
+}
+.component-stage > * {
+ max-width: 100%;
+}
+.preview-caption {
+ display: flex;
+ justify-content: space-between;
+ padding: 10px 16px;
+ color: var(--muted-foreground);
+ font-size: 14px;
+ background: var(--card);
+ border-top: 1px solid var(--border);
+}
+.doc-preview .code-block {
+ border: none;
+ border-radius: 0;
+}
+.doc-preview .code-block pre {
+ max-height: 560px;
+}
+.doc-section {
+ padding-top: 40px;
+ min-width: 0;
+}
+.doc-section h2 {
+ font-size: 24px;
+ font-weight: 500;
+ letter-spacing: -0.7px;
+ margin: 0 0 16px;
}
-
-.syntax-command,
-.syntax-symbol {
+.doc-section p {
+ color: var(--muted-foreground);
+ font-size: 15px;
+ line-height: 1.75;
+ margin: 0 0 20px;
+}
+.doc-section p a {
color: var(--foreground);
- font-weight: 750;
+ text-decoration: underline;
+ text-underline-offset: 3px;
}
-
-.syntax-keyword {
+.doc-section p code {
+ font-size: 14px;
color: var(--presence);
+ overflow-wrap: anywhere;
}
-
-.syntax-package {
- color: color-mix(in srgb, var(--presence) 72%, var(--foreground));
+.doc-section > .code-block {
+ margin-bottom: 24px;
}
-
-.syntax-string {
- color: color-mix(in srgb, var(--information) 70%, var(--foreground));
+.state-list {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 8px;
+ padding-block: 12px;
}
-
-.syntax-punctuation {
+.state-list > span:first-child {
color: var(--muted-foreground);
+ padding-right: 8px;
}
-
-.specimen-install-meta {
- display: grid;
- grid-template-columns: auto minmax(0, 1fr);
- gap: 0.75rem;
+.docs-contents {
+ position: sticky;
+ top: 116px;
+ align-self: start;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding-top: 40px;
color: var(--muted-foreground);
+ font-size: 14px;
+}
+.docs-contents > span {
+ color: var(--foreground);
+ font-weight: 500;
}
-
-.specimen-install-meta span {
+.docs-contents a:hover {
color: var(--presence);
- font-size: 0.625rem;
- font-weight: 750;
- letter-spacing: 0.08em;
- text-transform: uppercase;
}
-
-.run-spine {
- position: relative;
+.docs-contents > div {
+ border-top: 1px solid var(--border);
+ padding-top: 24px;
+ margin-top: 16px;
}
-
-.run-spine::before {
- position: absolute;
- inset-block: 0.5rem;
- inset-inline-start: 1.1rem;
- width: 1px;
- background: color-mix(in srgb, var(--presence) 38%, var(--border));
- content: "";
+.docs-contents > div > svg {
+ width: 20px;
+ height: 20px;
+ color: var(--presence);
}
-
-.catalog-empty {
- display: grid;
- min-height: 24rem;
- place-items: center;
- align-content: center;
- border: 1px dashed var(--border);
- border-radius: var(--radius-3);
- padding: 2rem;
- text-align: center;
+.docs-contents > div p {
+ margin-block: 12px;
+}
+.docs-contents > div a {
+ display: inline-flex;
+ align-items: center;
+ gap: 3px;
+}
+.docs-contents > div a svg {
+ width: 14px;
+ height: 14px;
+}
+.catalog-section {
+ padding-top: 40px;
}
-
-.catalog-empty__mark {
+.catalog-section > h2 {
+ font-size: 20px;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding-bottom: 16px;
+}
+.catalog-section > h2 > span {
+ color: var(--muted-foreground);
+ font-size: 14px;
+}
+.docs-catalog-grid {
display: grid;
- width: 2.75rem;
- height: 2.75rem;
- place-items: center;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 12px;
+}
+.catalog-tile {
+ display: block;
border: 1px solid var(--border);
- border-radius: var(--radius-2);
- color: var(--presence);
+ border-radius: 8px;
+ padding: 20px;
+ background: var(--card);
+ transition: border-color 150ms;
}
-
-.catalog-empty__mark svg {
- width: 1rem;
- height: 1rem;
+.catalog-tile:hover {
+ border-color: var(--presence);
}
-
-.catalog-empty h2 {
- margin: 1rem 0 0;
- font-size: 1.125rem;
+.catalog-tile > div {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12px;
}
-
-.catalog-empty p {
- margin: 0.4rem 0 0;
+.catalog-tile h3 {
+ font-size: 16px;
+ font-weight: 500;
+}
+.catalog-tile svg {
+ width: 15px;
+ height: 15px;
color: var(--muted-foreground);
- font-size: 0.8125rem;
+ flex-shrink: 0;
}
-
-.assembled-lab {
- overflow: hidden;
- scroll-margin-block-start: 0;
+.catalog-tile p {
+ font-size: 14px;
+ color: var(--muted-foreground);
+ margin-top: 10px;
+ line-height: 1.6;
+}
+.docs-pagination {
+ display: flex;
+ justify-content: space-between;
+ border-top: 1px solid var(--border);
+ padding-top: 24px;
+ margin-top: 48px;
+}
+.docs-pagination a {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+.docs-pagination a:last-child {
+ text-align: right;
+}
+.docs-pagination svg {
+ width: 16px;
+ height: 16px;
+}
+.docs-pagination small {
+ display: block;
+ color: var(--muted-foreground);
+ font-size: 14px;
+}
+.docs-mobile-toggle {
+ display: none;
+}
+.intro-callout {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
border: 1px solid var(--border);
- border-radius: var(--radius-3);
+ border-radius: 8px;
+ padding: 24px;
background: var(--card);
- color: var(--card-foreground);
- box-shadow: var(--elevation-2);
+ margin-top: 32px;
}
-
-.assembled-lab__nav {
- overflow-x: auto;
- border-block-end: 1px solid var(--border);
- padding: 0.75rem 1rem 0;
+.intro-callout svg {
+ width: 22px;
+ height: 22px;
+ flex-shrink: 0;
+ color: var(--presence);
}
-
-.assembled-lab__tabs {
- width: max-content;
- min-width: 0;
+.intro-callout p {
+ margin: 0;
+ color: var(--muted-foreground);
+ font-size: 15px;
+ line-height: 1.7;
}
-
-.assembled-lab__tabs [data-slot="tabs-trigger"] {
- flex: none;
+.guide-links {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 12px;
+ padding-bottom: 36px;
}
-
-.assembled-lab [data-slot="tabs"],
-.assembled-lab__stage,
-.lab-composer,
-.lab-message-stack {
- min-width: 0;
+.guide-links a {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+ border: 1px solid var(--border);
+ padding: 20px;
+ border-radius: 8px;
}
-
-.assembled-lab__stage {
- display: grid;
- min-height: 27rem;
- align-content: center;
- background: color-mix(in srgb, var(--muted) 42%, var(--background));
- padding: clamp(1rem, 3vw, 2rem);
-}
-
-.lab-composer,
-.lab-message-stack {
+.guide-links p {
+ margin: 0;
+ font-size: 14px;
+}
+.guide-links svg {
+ width: 16px;
+ height: 16px;
+}
+.guide-layer {
+ padding-block: 16px;
+}
+.guide-layer h3 {
+ font-size: 16px;
+ font-weight: 500;
+ padding-bottom: 8px;
+}
+.token-swatches {
display: grid;
- width: min(100%, 48rem);
- gap: 1.25rem;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 12px;
+ padding-bottom: 24px;
+}
+.token-swatches > div {
+ min-width: 0;
+}
+.token-swatches span {
+ display: block;
+ height: 70px;
+ border-radius: 7px;
+ border: 1px solid var(--border);
+}
+.token-swatches code {
+ display: block;
+ font-size: 14px;
+ color: var(--muted-foreground);
+ padding-top: 8px;
+ overflow-wrap: anywhere;
+}
+.lab-main {
+ max-width: 1100px;
+ padding: 64px 40px 80px;
margin-inline: auto;
+ min-height: 80vh;
+}
+.lab-main .docs-heading em {
+ color: var(--presence);
+}
+.lab-surface {
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ margin-top: 40px;
+ overflow: hidden;
+}
+.lab-tabs {
+ border-bottom: 1px solid var(--border);
+ padding: 12px 20px;
+ overflow-x: auto;
+}
+.lab-stage {
+ display: flex;
+ flex-direction: column;
+ gap: 28px;
+ padding: 40px;
+ min-height: 280px;
}
-
.lab-card-grid {
display: grid;
- width: min(100%, 52rem);
grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 0.875rem;
- margin-inline: auto;
+ gap: 20px;
+ padding: 32px;
}
-
-.assembled-lab__stage > [data-slot="card"] {
- width: min(100%, 48rem);
- margin-inline: auto;
+.lab-caption {
+ color: var(--muted-foreground);
+ text-align: center;
+ margin-top: 24px;
}
-
-@media (max-width: 68rem) {
- .specimen-topbar__inner {
- grid-template-columns: auto 1fr;
- padding-block: 0.75rem;
+@media (min-width: 1400px) {
+ .home-hero {
+ padding-top: 105px;
+ padding-bottom: 80px;
}
-
- .surface-switcher {
- justify-self: end;
+}
+@media (max-width: 1199px) {
+ .site-header-inner {
+ gap: 32px;
+ padding-inline: 32px;
}
-
- .specimen-topbar__actions {
- display: grid;
- width: 100%;
- grid-column: 1 / -1;
- grid-template-columns: minmax(0, 1fr) auto auto;
+ .docs-layout {
+ grid-template-columns: 190px minmax(0, 1fr);
+ gap: 32px;
+ padding-inline: 32px;
}
-
- .specimen-search {
- width: 100%;
+ .docs-contents {
+ display: none;
}
-
- .specimen-shell {
- min-height: 0;
- grid-template-columns: 1fr;
+ .site-footer {
+ flex-wrap: wrap;
}
-
- .specimen-rail {
- position: static;
- display: block;
- height: auto;
- overflow-x: auto;
- border-inline-end: 0;
- border-block-end: 1px solid var(--border);
- padding: 0.5rem clamp(1rem, 4vw, 2rem);
- }
-
- .specimen-rail__context,
- .specimen-rail__package {
+ .site-footer nav {
+ gap: 16px;
+ }
+}
+@media (max-width: 1000px) {
+ .site-header-inner {
+ gap: 28px;
+ }
+ .primary-nav {
+ gap: 20px;
+ }
+ .site-search {
+ width: 175px;
+ }
+ .home-main {
+ padding-inline: 32px;
+ }
+ .showcase-workspace {
+ grid-template-columns: minmax(0, 1fr) 225px;
+ }
+ .showcase-evidence {
+ padding-inline: 20px;
+ }
+ .home-component-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ .site-footer > p {
+ display: none;
+ }
+}
+@media (max-width: 767px) {
+ .site-header-inner {
+ min-height: 64px;
+ padding-inline: 20px;
+ gap: 20px;
+ }
+ .site-brand > span {
+ font-size: 23px;
+ }
+ .site-brand > svg {
+ width: 28px;
+ height: 28px;
+ }
+ .header-tools {
+ gap: 6px;
+ }
+ .site-search {
+ width: 160px;
+ }
+ .site-search kbd {
display: none;
}
-
- .specimen-rail__nav {
+ .github-link {
+ display: none;
+ }
+ .mobile-nav-button {
+ display: inline-flex;
+ }
+ .primary-nav {
+ display: none;
+ }
+ .primary-nav[data-open="true"] {
+ position: absolute;
+ top: 64px;
+ left: 0;
+ right: 0;
display: flex;
- width: max-content;
- min-width: 100%;
- gap: 0.375rem;
- margin: 0;
- }
-
- .specimen-rail__nav a {
- min-width: 9rem;
- flex: 1 0 auto;
- border: 1px solid transparent;
- background: color-mix(in srgb, var(--card) 72%, var(--background));
- }
-
- .specimen-main__inner {
- padding-block-start: clamp(1.5rem, 4vw, 2.5rem);
- }
-}
-
-@media (max-width: 48rem) {
- .specimen-hero {
- grid-template-columns: 1fr;
- align-items: start;
- }
-
- .specimen-stats {
+ flex-direction: column;
+ align-items: stretch;
+ padding: 20px;
+ background: var(--card);
+ border-bottom: 1px solid var(--border);
+ }
+ .primary-nav a {
+ padding: 8px;
+ }
+ .home-main {
+ padding-inline: 20px;
+ }
+ .home-hero {
+ padding-block: 62px 46px;
+ }
+ .home-hero h1 {
+ font-size: clamp(44px, 8.8vw, 65px);
+ letter-spacing: -2.7px;
+ line-height: 1.13;
+ margin-top: 28px;
+ }
+ .home-hero h1 em {
+ letter-spacing: -2.4px;
+ }
+ .hero-description {
+ font-size: 15px;
+ max-width: 440px;
+ }
+ .release-note {
+ gap: 7px;
+ font-size: 14px;
+ }
+ .hero-cta {
+ padding-inline: 15px;
+ }
+ .hero-foundations {
+ gap: 12px;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
+ .hero-install {
width: 100%;
}
-
- .specimen-hero {
- gap: 1.25rem;
- margin-block-end: 2rem;
- padding-block-end: 1.25rem;
+ .code-block--compact pre {
+ min-width: 0;
+ font-size: 14px;
}
-
- .catalog-group__header {
- align-items: start;
+ .section-overline {
+ font-size: 14px;
}
-
- .specimen-grid {
- grid-template-columns: 1fr;
+ .section-overline > span:last-child {
+ display: none;
}
-
- .specimen-card__header {
- min-height: 0;
+ .showcase-title {
+ padding: 14px 16px;
}
-}
-
-@media (max-width: 36rem) {
- .specimen-topbar__inner {
- gap: 0.75rem;
+ .showcase-title > span:first-child {
+ font-size: 14px;
+ }
+ .showcase-title [data-slot="badge"] {
+ display: none;
}
-
- .specimen-brand small {
+ .showcase-workspace {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ .showcase-evidence {
display: none;
}
-
- .specimen-topbar__actions {
- grid-template-columns: minmax(0, 1fr) auto auto;
+ .showcase-conversation {
+ padding: 18px 18px 10px;
}
-
- .specimen-search {
- grid-column: auto;
+ .showcase-context {
+ font-size: 14px;
}
-
- .scheme-control span {
+ .showcase-context span {
display: none;
}
-
- .specimen-main__inner {
- padding-inline: 1rem;
+ .showcase-footnote {
+ font-size: 14px;
}
-
- .specimen-hero {
- margin-block-end: 2rem;
+ .showcase-caption {
+ gap: 16px;
+ font-size: 14px;
}
-
- .specimen-hero h1 {
- font-size: clamp(2.25rem, 11vw, 3.25rem);
+ .showcase-caption > span {
+ display: none;
}
-
- .catalog {
- gap: 3rem;
+ .home-library {
+ padding-top: 64px;
}
-
- .catalog-group__header {
- display: grid;
- gap: 0.75rem;
+ .section-heading {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 20px;
}
-
- .catalog-group__count {
- justify-self: start;
+ .section-heading h2 {
+ font-size: 30px;
}
-
- .specimen-stage,
- .assembled-lab__stage {
- padding: 1rem;
+ .home-component-grid {
+ gap: 12px;
}
-
- .assembled-lab__stage {
- min-height: 24rem;
+ .home-component-preview {
+ padding: 16px;
+ min-height: 190px;
}
-
- .lab-card-grid {
- grid-template-columns: 1fr;
+ .principles {
+ grid-template-columns: minmax(0, 1fr);
+ gap: 32px;
+ padding-block: 54px;
}
-}
-
-@media (max-width: 24.375rem) {
- html {
- scroll-padding-top: 1rem;
+ .home-closing {
+ padding-block: 48px 64px;
+ }
+ .home-closing h2 {
+ font-size: 43px;
+ }
+ .site-footer {
+ padding: 24px 20px;
+ gap: 20px;
+ }
+ .site-footer nav {
+ margin-left: 0;
+ flex-wrap: wrap;
+ }
+ .site-footer nav > span {
+ display: none;
+ }
+ .docs-layout {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+ padding-inline: 20px;
}
-
- .specimen-topbar {
+ .docs-mobile-toggle {
+ display: block;
+ padding-top: 20px;
+ }
+ .docs-sidebar {
+ display: none;
position: static;
+ height: auto;
+ max-height: 65vh;
+ padding-top: 24px;
+ padding-right: 0;
+ }
+ .docs-sidebar[data-open="true"] {
+ display: block;
+ }
+ .docs-main {
+ padding-top: 28px;
+ }
+ .docs-heading h1 {
+ font-size: 40px;
}
-
- .specimen-brand > span:last-child {
+ .component-stage {
+ min-height: 240px;
+ padding: 24px 16px;
+ }
+ .preview-caption {
+ font-size: 14px;
+ }
+ .preview-caption span {
display: none;
}
-
- .specimen-brand__mark {
- width: 2rem;
- height: 2rem;
+ .docs-catalog-grid {
+ grid-template-columns: minmax(0, 1fr);
}
-
- .surface-switcher a,
- .density-control button {
- padding-inline: 0.45rem;
- font-size: 0.6875rem;
+ .lab-main {
+ padding: 40px 20px 60px;
}
-
- .specimen-topbar__actions {
- gap: 0.375rem;
+ .lab-stage {
+ padding: 24px 16px;
}
-
- .specimen-hero h1 {
- font-size: 2rem;
+ .lab-card-grid {
+ padding: 20px 16px;
+ grid-template-columns: minmax(0, 1fr);
}
-
- .specimen-main__inner {
- padding-block-start: 16px;
+}
+@media (max-width: 479px) {
+ .site-search {
+ width: 130px;
+ padding-inline: 8px;
+ gap: 6px;
+ }
+ .site-header-inner {
padding-inline: 16px;
}
-
- .specimen-hero {
- gap: 1rem;
- margin-block-end: 1.5rem;
+ .site-brand > span {
+ font-size: 21px;
}
-
- .specimen-hero__copy > p:last-child {
- font-size: 0.8125rem;
- line-height: 1.5;
+ .brand-ui {
+ font-size: 14px;
}
-
- .specimen-stats div {
- min-width: 0;
- padding-inline-start: 0.625rem;
+ .header-tools {
+ gap: 2px;
+ }
+ .home-main {
+ padding-inline: 20px;
}
-
- .specimen-stats dt {
- font-size: 0.55rem;
+ .home-hero h1 {
+ font-size: 43px;
}
-
- .specimen-stage {
- min-height: 14rem;
- padding: 0.875rem;
+ .release-note > .release-divider,
+ .release-note > .release-divider + span {
+ display: none;
+ }
+ .desktop-break {
+ display: none;
+ }
+ .hero-actions {
+ gap: 8px;
+ }
+ .hero-cta {
+ padding-inline: 12px;
+ font-size: 14px;
+ }
+ .home-component-grid {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ .guide-links {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ .token-swatches {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
}
-
- .specimen-documentation {
- min-height: 14rem;
- padding: 1rem;
+ .search-results {
+ position: fixed;
+ top: 60px;
+ left: 16px;
+ right: 16px;
+ width: auto;
+ }
+ .docs-pagination {
+ gap: 12px;
}
}
-
-@media (prefers-reduced-motion: reduce) {
- .skip-link {
- transition: none;
+@media (max-width: 379px) {
+ .site-header-inner {
+ gap: 8px;
+ }
+ .site-search {
+ width: 96px;
}
-
- .specimen-card {
- translate: none;
+ .home-hero h1 {
+ font-size: 37px;
+ }
+ .hero-actions {
+ flex-direction: column;
+ align-items: stretch;
+ width: 100%;
+ }
+ .release-note {
+ flex-wrap: wrap;
+ justify-content: center;
}
}
-
-@media (forced-colors: active) {
- .surface-switcher a[aria-current="page"],
- .density-control button[aria-pressed="true"] {
- border: 1px solid Highlight;
+@media (prefers-reduced-motion: no-preference) {
+ .home-hero {
+ animation: appear 650ms ease-out both;
+ }
+ .showcase-section {
+ animation: appear 650ms 100ms ease-out both;
+ }
+ @keyframes appear {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
}
}
diff --git a/apps/specimens/vite.config.ts b/apps/specimens/vite.config.ts
index 0bc1d26..66b5719 100644
--- a/apps/specimens/vite.config.ts
+++ b/apps/specimens/vite.config.ts
@@ -4,6 +4,7 @@ import { defineConfig } from "vite";
export default defineConfig({
plugins: [react(), tailwindcss()],
+ server: { host: "0.0.0.0" },
// The generated shadcn registry lives in the workspace-root `public/` so that
// `shadcn build` and the deployed site publish the same files.
publicDir: new URL("../../public", import.meta.url).pathname,
diff --git a/packages/ui/src/components/ui/dropdown-menu.tsx b/packages/ui/src/components/ui/dropdown-menu.tsx
index 398bfbf..1d32418 100644
--- a/packages/ui/src/components/ui/dropdown-menu.tsx
+++ b/packages/ui/src/components/ui/dropdown-menu.tsx
@@ -17,6 +17,7 @@ function DropdownMenuContent({
side = "bottom",
sideOffset = 6,
className,
+ children,
...props
}: MenuPrimitive.Popup.Props &
Pick) {
@@ -35,7 +36,9 @@ function DropdownMenuContent({
className,
)}
{...props}
- />
+ >
+ {children}
+
);
diff --git a/packages/ui/tests/components.test.tsx b/packages/ui/tests/components.test.tsx
index b06cfe5..d88c928 100644
--- a/packages/ui/tests/components.test.tsx
+++ b/packages/ui/tests/components.test.tsx
@@ -8,6 +8,7 @@ import { axe } from "vitest-axe";
import { Composer } from "@opencoven/ui/blocks/composer";
import { AttachmentChip } from "@opencoven/ui/components/attachment-chip";
import { ModeSwitch } from "@opencoven/ui/components/mode-switch";
+import { CompletionPalette } from "@opencoven/ui/components/completion-palette";
import { ToolClassBadge } from "@opencoven/ui/components/tool-class-badge";
import { Button } from "@opencoven/ui/components/ui/button";
@@ -22,6 +23,26 @@ describe("OpenCoven UI", () => {
expect(onClick).toHaveBeenCalledOnce();
});
+ it("opens labeled slash commands and selects a command without a missing group context", async () => {
+ const user = userEvent.setup();
+ const onSelect = vi.fn();
+ const command = {
+ id: "plan",
+ label: "/plan",
+ description: "Think before acting",
+ };
+ render(
+ Open commands}
+ commands={[command]}
+ onSelect={onSelect}
+ />,
+ );
+ await user.click(screen.getByRole("button", { name: "Open commands" }));
+ await user.click(await screen.findByRole("menuitem", { name: /\/plan/ }));
+ expect(onSelect).toHaveBeenCalledWith(command);
+ });
+
it("exposes typed mode state with a non-color pressed cue", () => {
const onValueChange = vi.fn();
render( );
diff --git a/public/r/dropdown-menu.json b/public/r/dropdown-menu.json
index f4ceb4a..0858fac 100644
--- a/public/r/dropdown-menu.json
+++ b/public/r/dropdown-menu.json
@@ -13,7 +13,7 @@
"files": [
{
"path": "packages/ui/src/components/ui/dropdown-menu.tsx",
- "content": "\"use client\";\n\nimport { Menu as MenuPrimitive } from \"@base-ui/react/menu\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction DropdownMenu(props: MenuPrimitive.Root.Props) {\n return ;\n}\n\nfunction DropdownMenuTrigger(props: MenuPrimitive.Trigger.Props) {\n return ;\n}\n\nfunction DropdownMenuContent({\n align = \"start\",\n side = \"bottom\",\n sideOffset = 6,\n className,\n ...props\n}: MenuPrimitive.Popup.Props &\n Pick) {\n return (\n \n \n \n \n \n );\n}\n\nfunction DropdownMenuLabel({\n className,\n ...props\n}: MenuPrimitive.GroupLabel.Props) {\n return (\n \n );\n}\n\nfunction DropdownMenuItem({ className, ...props }: MenuPrimitive.Item.Props) {\n return (\n \n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n};\n",
+ "content": "\"use client\";\n\nimport { Menu as MenuPrimitive } from \"@base-ui/react/menu\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction DropdownMenu(props: MenuPrimitive.Root.Props) {\n return ;\n}\n\nfunction DropdownMenuTrigger(props: MenuPrimitive.Trigger.Props) {\n return ;\n}\n\nfunction DropdownMenuContent({\n align = \"start\",\n side = \"bottom\",\n sideOffset = 6,\n className,\n children,\n ...props\n}: MenuPrimitive.Popup.Props &\n Pick) {\n return (\n \n \n \n {children} \n \n \n \n );\n}\n\nfunction DropdownMenuLabel({\n className,\n ...props\n}: MenuPrimitive.GroupLabel.Props) {\n return (\n \n );\n}\n\nfunction DropdownMenuItem({ className, ...props }: MenuPrimitive.Item.Props) {\n return (\n \n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuTrigger,\n};\n",
"type": "registry:ui",
"target": "@ui/dropdown-menu.tsx"
}
diff --git a/scripts/verify-contracts.mjs b/scripts/verify-contracts.mjs
index c1fb5a8..b7bc2a2 100644
--- a/scripts/verify-contracts.mjs
+++ b/scripts/verify-contracts.mjs
@@ -41,9 +41,20 @@ const manifest = JSON.parse(packageJson);
const portable = JSON.parse(portableJson);
const vectors = JSON.parse(vectorsJson);
const specimenStyles = `${specimenCss}\n${specimenFixes}`;
-const specimenAt68 = specimenCss.slice(
- specimenCss.indexOf("@media (max-width: 68rem)"),
- specimenCss.indexOf("@media (max-width: 48rem)"),
+const [home, docs, catalog, codeBlock, examples, lab, main, registryJson] =
+ await Promise.all([
+ read("apps/specimens/src/home.tsx"),
+ read("apps/specimens/src/docs.tsx"),
+ read("apps/specimens/src/catalog.ts"),
+ read("apps/specimens/src/code-block.tsx"),
+ read("apps/specimens/src/examples.tsx"),
+ read("apps/specimens/src/lab.tsx"),
+ read("apps/specimens/src/main.tsx"),
+ read("registry.json"),
+ ]);
+const siteMarkup = [specimenApp, home, docs, codeBlock, lab].join("\n");
+const visualItems = JSON.parse(registryJson).items.filter((item) =>
+ ["registry:ui", "registry:component", "registry:block"].includes(item.type),
);
const assertions = [
["style is base-nova", config.style === "base-nova"],
@@ -103,130 +114,103 @@ const assertions = [
),
],
[
- "specimen shell has stable landmarks",
- specimenApp.includes('className="specimen-topbar"') &&
- specimenApp.includes('className="specimen-rail"') &&
- specimenApp.includes('id="specimen-main"') &&
- specimenApp.includes('className="skip-link"'),
+ "site shell has stable landmarks",
+ specimenApp.includes('className="site-header"') &&
+ specimenApp.includes('className="skip-link"') &&
+ [home, docs, lab].every((source) => source.includes('id="main-content"')),
],
[
- "catalog restores task hierarchy",
- ["group-composer", "group-run-rail", "group-blocks"].every((id) =>
- specimenApp.includes(id),
- ) &&
- specimenApp.includes('className="catalog-group__summary"') &&
- specimenApp.includes("{group} "),
+ "catalog preserves task hierarchy",
+ ["Foundations", "Composer controls", "Run & evidence", "Blocks"].every(
+ (group) => catalog.includes(group),
+ ) && docs.includes("groups.map"),
],
[
- "install tab separates CLI from package API",
- specimenApp.includes(
- 'Install ',
- ) &&
- !specimenApp.includes('API ') &&
- specimenApp.includes("CLI ") &&
- specimenApp.includes("TypeScript ") &&
- specimenApp.includes("package API "),
+ "documentation separates registry and package consumption",
+ docs.includes('id="installation"') &&
+ docs.includes('label="Registry import"') &&
+ docs.includes("packagePath") &&
+ docs.includes("package imports"),
],
[
- "install snippets derive valid registry and package paths",
- specimenApp.includes(
- 'specimen.group === "Blocks" ? "blocks" : "components"',
- ) &&
- specimenApp.includes(
- "const registryUrl = `https://ui.opencoven.ai/r/${specimen.id}.json`;",
- ) &&
- specimenApp.includes(
- "const packagePath = `@opencoven/ui/${sourceKind}/${specimen.id}`;",
- ) &&
- specimenApp.includes('.split("-")') &&
- specimenApp.includes(".toUpperCase()"),
+ "install paths come from real registry metadata",
+ catalog.includes('"../../../registry.json?raw"') &&
+ catalog.includes("file.target") &&
+ catalog.includes('replace("packages/ui/src/", "@opencoven/ui/")') &&
+ catalog.includes("https://ui.opencoven.ai/r"),
],
[
- "install snippets use visible syntax roles",
- [
- "syntax-command",
- "syntax-keyword",
- "syntax-package",
- "syntax-string",
- "syntax-symbol",
- "syntax-punctuation",
- ].every(
- (className) =>
- specimenApp.includes(`className="${className}"`) &&
- specimenCss.includes(`.${className}`),
- ),
+ "every visual registry item has a working example export",
+ visualItems.every((item) => {
+ const name = item.name
+ .split("-")
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
+ .join("");
+ return examples.includes(`export function ${name}Example(`);
+ }),
+ ],
+ [
+ "code examples are grounded in rendered source",
+ catalog.includes('"./examples.tsx?raw"') &&
+ docs.includes("exampleCode(entry)") &&
+ docs.includes(" \[data-slot="tabs"\] > \[data-slot="tabs-list"\]\s*\{[^}]*grid-template-columns:\s*repeat\(3,\s*minmax\(5\.25rem,\s*1fr\)\);[^}]*overflow-x:\s*auto;/.test(
- specimenFixes,
- ),
+ "preview tabs use accessible primitives",
+ docs.includes('Preview ') &&
+ docs.includes('Code ') &&
+ docs.includes(''),
],
[
- "mobile tab selection and focus stay inside scrollport",
- /\[data-slot="tabs-trigger"\]\[data-active\]::after\s*\{[^}]*inset-block-end:\s*0 !important;/.test(
- specimenFixes,
- ) &&
- /\[data-slot="tabs-trigger"\]:focus-visible\s*\{[^}]*outline-offset:\s*-3px;/.test(
- specimenFixes,
- ),
+ "responsive previews remove intrinsic sizing floors",
+ specimenFixes.includes("min-width: 0") &&
+ specimenFixes.includes("max-width: 100%") &&
+ specimenFixes.includes("overflow-x: auto"),
],
[
- "text resize keeps shell chrome and hero contained",
- specimenFixes.includes(
- ".specimen-topbar__inner {\n display: flex;\n flex-wrap: wrap;",
- ) &&
- /\.specimen-topbar__actions\s*\{[^}]*display:\s*flex;[^}]*flex:\s*1 0 100%;[^}]*flex-wrap:\s*wrap;/.test(
- specimenFixes,
- ) &&
- /\.specimen-search\s*\{[^}]*width:\s*auto;[^}]*min-width:\s*7rem;[^}]*flex:\s*1 1 10rem;/.test(
- specimenFixes,
- ) &&
- specimenFixes.includes(
- ".specimen-main__inner {\n box-sizing: border-box;",
- ) &&
- specimenFixes.includes(
- ".specimen-stats {\n grid-template-columns: repeat(3, minmax(0, 1fr));",
- ),
+ "all five lab views remain available",
+ ["composer", "messages", "context", "actions", "cards"].every((name) =>
+ lab.includes(``),
+ ),
],
[
"specimen chrome avoids decorative gradients",
@@ -239,44 +223,29 @@ const assertions = [
];
const specimenSelectorPairs = [
+ ["header actions", 'className="header-tools"', ".header-tools"],
+ ["density control", 'className="density-select"', ".density-select"],
+ ["documentation rail", 'className="docs-sidebar"', ".docs-sidebar"],
[
- "topbar actions",
- 'className="specimen-topbar__actions"',
- ".specimen-topbar__actions",
- ],
- ["density control", 'className="density-control"', ".density-control"],
- ["scheme control", 'className="scheme-control"', ".scheme-control"],
- [
- "rail context",
- 'className="specimen-rail__context"',
- ".specimen-rail__context",
- ],
- ["rail kicker", 'className="specimen-kicker numeric"', ".specimen-kicker"],
- [
- "rail package",
- 'className="specimen-rail__package"',
- ".specimen-rail__package",
- ],
- ["hero", 'className="specimen-hero"', ".specimen-hero"],
- ["hero copy", 'className="specimen-hero__copy"', ".specimen-hero__copy"],
- ["hero stats", 'className="specimen-stats"', ".specimen-stats"],
- [
- "catalog eyebrow",
- 'className="catalog-group__eyebrow numeric"',
- ".catalog-group__eyebrow",
+ "mobile navigation",
+ 'className="docs-mobile-toggle"',
+ ".docs-mobile-toggle",
],
+ ["homepage hero", 'className="home-hero"', ".home-hero"],
+ ["live showcase", 'className="agent-showcase"', ".agent-showcase"],
[
- "catalog summary",
- 'className="catalog-group__summary"',
- ".catalog-group__summary",
+ "component gallery",
+ 'className="home-component-grid"',
+ ".home-component-grid",
],
- ["specimen grid", 'className="specimen-grid"', ".specimen-grid"],
+ ["documentation preview", 'className="component-stage"', ".component-stage"],
+ ["search results", 'className="search-results"', ".search-results"],
];
for (const [name, markup, selector] of specimenSelectorPairs) {
assertions.push([
`${name} markup and CSS stay paired`,
- specimenApp.includes(markup) && specimenStyles.includes(selector),
+ siteMarkup.includes(markup) && specimenStyles.includes(selector),
]);
}
diff --git a/scripts/verify-site.mjs b/scripts/verify-site.mjs
new file mode 100644
index 0000000..441b83d
--- /dev/null
+++ b/scripts/verify-site.mjs
@@ -0,0 +1,157 @@
+/* global document, window */
+import assert from "node:assert/strict";
+import { mkdir } from "node:fs/promises";
+
+const { chromium } = await import(process.argv[2] ?? "playwright");
+const browser = await chromium.launch({
+ headless: true,
+ args: ["--no-sandbox", "--disable-dev-shm-usage"],
+});
+const context = await browser.newContext({
+ viewport: { width: 937, height: 795 },
+ colorScheme: "dark",
+});
+const page = await context.newPage();
+page.setDefaultTimeout(10000);
+const errors = [];
+page.on("pageerror", (error) => errors.push(error.message));
+const origin = process.env.BASE_URL ?? "http://127.0.0.1:5173";
+await mkdir("/tmp/agent-browser", { recursive: true });
+try {
+ await page.goto(origin);
+ await page
+ .getByRole("heading", { name: "Built for agents. Made for humans." })
+ .waitFor();
+ await page.waitForTimeout(800);
+ await page.screenshot({
+ path: "/tmp/agent-browser/coven-verified-desktop.png",
+ });
+ await page.getByRole("button", { name: "Send", exact: true }).click();
+ await page.getByText("Received locally. No model connected.").waitFor();
+ await page.getByRole("button", { name: "Reset showcase" }).click();
+ await page.setViewportSize({ width: 390, height: 844 });
+ await page.evaluate(() => window.scrollTo(0, 0));
+ await page.waitForTimeout(300);
+ await page.screenshot({
+ path: "/tmp/agent-browser/coven-verified-mobile.png",
+ animations: "disabled",
+ });
+ assert.equal(
+ await page.evaluate(
+ () => document.documentElement.scrollWidth <= window.innerWidth,
+ ),
+ true,
+ "Mobile home must not overflow",
+ );
+ await page.goto(`${origin}/docs/components/composer`);
+ await page.getByRole("heading", { name: "Composer", exact: true }).waitFor();
+ await page.getByRole("tab", { name: "Code", exact: true }).click();
+ assert.match(
+ await page.locator("pre").first().innerText(),
+ /import \{ useState \} from "react"/,
+ );
+ await context.grantPermissions(["clipboard-read", "clipboard-write"]);
+ await page
+ .getByRole("button", { name: "Copy Terminal", exact: true })
+ .click();
+ assert.match(
+ await page.evaluate(() => navigator.clipboard.readText()),
+ /\/r\/composer.json/,
+ );
+ await page.getByRole("tab", { name: "Preview", exact: true }).click();
+ await page.getByLabel("Preview density").selectOption("compact");
+ await page.reload();
+ assert.equal(
+ await page.getByLabel("Preview density").inputValue(),
+ "compact",
+ );
+ await page.getByRole("button", { name: "Documentation menu" }).click();
+ await page.getByRole("link", { name: "Button", exact: true }).click();
+ await page.getByRole("button", { name: "Save changes" }).click();
+ await page.getByRole("button", { name: "Changes saved" }).waitFor();
+ await page.keyboard.press("Control+k");
+ await page.getByLabel("Search documentation").fill("no-such-component");
+ await page.getByText("No matching pages. Try “composer”.").waitFor();
+ await page.getByLabel("Search documentation").fill("context");
+ await page.keyboard.press("ArrowDown");
+ await page.keyboard.press("Enter");
+ await page
+ .getByRole("heading", { name: "Context meter", exact: true })
+ .waitFor();
+ await page.setViewportSize({ width: 937, height: 795 });
+ await page.screenshot({ path: "/tmp/agent-browser/coven-docs-desktop.png" });
+ await page.getByRole("button", { name: "Use light scheme" }).click();
+ await page.screenshot({ path: "/tmp/agent-browser/coven-docs-light.png" });
+ await page.goto(`${origin}/lab`);
+ for (const name of ["Messages", "Context", "Actions", "Cards", "Composer"]) {
+ await page.getByRole("tab", { name, exact: true }).click();
+ assert.equal(
+ await page
+ .getByRole("tab", { name, exact: true })
+ .getAttribute("aria-selected"),
+ "true",
+ );
+ }
+ await page.goto(`${origin}/docs/components`);
+ const links = await page
+ .locator(".catalog-tile")
+ .evaluateAll((nodes) => nodes.map((node) => node.getAttribute("href")));
+ await page.setViewportSize({ width: 390, height: 844 });
+ for (const link of links) {
+ await page.goto(`${origin}${link}`);
+ await page.locator(".component-stage").waitFor();
+ assert.ok(
+ await page.locator(".component-stage").innerHTML(),
+ `${link} needs a preview`,
+ );
+ assert.equal(
+ await page.evaluate(
+ () => document.documentElement.scrollWidth <= window.innerWidth,
+ ),
+ true,
+ `${link} must not overflow on mobile`,
+ );
+ }
+ await page.goto(`${origin}/docs/components/completion-palette`);
+ await page.getByRole("button", { name: "Open slash commands" }).click();
+ await page.getByRole("menuitem", { name: /\/plan/ }).click();
+ assert.equal(
+ await page.locator(".component-stage [role=status]").innerText(),
+ "/plan",
+ );
+ await page.goto(`${origin}/docs/components/dropdown-menu`);
+ await page.getByRole("button", { name: "Open menu" }).click();
+ await page.getByRole("menuitem", { name: "Duplicate" }).click();
+ await page.getByText("Duplicate selected").waitFor();
+ await page.screenshot({
+ path: "/tmp/agent-browser/coven-docs-mobile-final.png",
+ animations: "disabled",
+ });
+ await page.goto(origin);
+ await page.emulateMedia({ reducedMotion: "reduce" });
+ await page.setViewportSize({ width: 320, height: 700 });
+ assert.equal(
+ await page.evaluate(
+ () => document.documentElement.scrollWidth <= window.innerWidth,
+ ),
+ true,
+ "320px homepage must not overflow",
+ );
+ await page.setViewportSize({ width: 1440, height: 1000 });
+ await page.screenshot({
+ path: "/tmp/agent-browser/coven-home-light-full.png",
+ fullPage: true,
+ animations: "disabled",
+ });
+ assert.deepEqual(errors, [], "No browser runtime errors");
+ console.log(
+ `Verified home, docs search, code tabs, density, themes, lab, and ${links.length} component routes.`,
+ );
+} catch (error) {
+ console.error(errors);
+ console.error(await page.locator("body").ariaSnapshot());
+ await page.screenshot({ path: "/tmp/agent-browser/coven-test-failure.png" });
+ throw error;
+} finally {
+ await browser.close();
+}
diff --git a/vercel.json b/vercel.json
index 3dac685..d6d46a9 100644
--- a/vercel.json
+++ b/vercel.json
@@ -3,6 +3,19 @@
"installCommand": "pnpm install --frozen-lockfile",
"buildCommand": "pnpm build",
"outputDirectory": "apps/specimens/dist",
+ "headers": [
+ {
+ "source": "/(.*)",
+ "headers": [
+ { "key": "X-Content-Type-Options", "value": "nosniff" },
+ {
+ "key": "Referrer-Policy",
+ "value": "strict-origin-when-cross-origin"
+ },
+ { "key": "Strict-Transport-Security", "value": "max-age=63072000" }
+ ]
+ }
+ ],
"rewrites": [
{
"source": "/((?!r/).*)",