From fe0e26770b6417a2b96897cd323173f25d9b48a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Bokisch?= Date: Thu, 21 May 2026 17:17:56 +0200 Subject: [PATCH] docs: open graph + twitter cards + site metadata (social-kit port) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports the OG card from the Claude Design social-kit handoff (1BcwLvogEyTm8D4LpOGcSw) into the docs site, plus the surrounding metadata that platforms use to render link previews. **Metadata (`layout.tsx`)** - `metadataBase` → `https://vitus-labs.com` (overridable via `NEXT_PUBLIC_SITE_URL`) - `title` template + default `Vitus Labs — Composable React engine` - `description` includes the measured numbers: 15 packages, 4.82 KB CSS-in-JS engine, 170+ CSS props, 123 motion presets - `openGraph` block (type, locale, url, siteName, title, description) - `twitter` block (`summary_large_image`, creator handle) - `robots`, `alternates.canonical`, `keywords`, `category`, `authors` - `viewport.themeColor` per color-scheme (`#0a0b0d` dark, `#fdfbf5` light) and `colorScheme: 'dark light'` **OG image (`opengraph-image.tsx`)** - 1200×630 PNG generated at build time via `next/og` `ImageResponse` (with `dynamic = 'force-static'` so the static export emits a real PNG to `/out/opengraph-image` instead of needing a runtime). - Stacked-triangle mark + `vitus·labs` lockup top-left. - Headline `Build, style & ship faster.` with chartreuse italic on `faster.` — same wording as the hero. - Measured subtitle row. - Orbital motif (four rings + center disc + accent node) on the right. - Radial chartreuse + cobalt aurora glow with faint dot-grid behind. - Footer: `vitus-labs.com` · `MIT · TYPESCRIPT-FIRST`. **Twitter image (`twitter-image.tsx`)** - Same 1200×630 artwork — Twitter's `summary_large_image` uses the same dimensions, so it re-uses the default export from `opengraph-image.tsx`. Route-segment config (`dynamic`, `alt`, `size`, `contentType`) is inlined per file because Next.js parses it statically and rejects re-exported config. Co-Authored-By: Claude Opus 4.7 --- src/app/layout.tsx | 64 ++++++++- src/app/opengraph-image.tsx | 279 ++++++++++++++++++++++++++++++++++++ src/app/twitter-image.tsx | 16 +++ 3 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 src/app/opengraph-image.tsx create mode 100644 src/app/twitter-image.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 34eb3fa..48043f4 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,15 +1,73 @@ import { RootProvider } from 'fumadocs-ui/provider/next' -import type { Metadata } from 'next' +import type { Metadata, Viewport } from 'next' import type { ReactNode } from 'react' import './global.css' +const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? 'https://vitus-labs.com' + export const metadata: Metadata = { + metadataBase: new URL(SITE_URL), title: { template: '%s | Vitus Labs', - default: 'Vitus Labs — React UI & Developer Tools', + default: 'Vitus Labs — Composable React engine', }, description: - 'A modular ecosystem for building, styling, testing, and shipping React applications — from UI primitives to developer tooling.', + 'A modular React ecosystem — 15 packages, a 4.82 KB CSS-in-JS engine, 170+ CSS prop descriptors, 123 motion presets. Build, style, and ship React apps faster.', + applicationName: 'Vitus Labs', + authors: [{ name: 'Vít Bokisch', url: 'https://github.com/vitus-labs' }], + keywords: [ + 'react', + 'ui', + 'design system', + 'css-in-js', + 'styled components', + 'monorepo', + 'typescript', + 'animation', + 'react native', + 'vitus labs', + 'styler', + 'rocketstyle', + 'kinetic', + ], + category: 'technology', + openGraph: { + type: 'website', + locale: 'en_US', + url: SITE_URL, + siteName: 'Vitus Labs', + title: 'Vitus Labs — Composable React engine', + description: + '15 packages · 4.82 KB CSS-in-JS engine · 170+ CSS props · 123 motion presets. One ecosystem, zero glue code.', + }, + twitter: { + card: 'summary_large_image', + title: 'Vitus Labs — Composable React engine', + description: + '15 packages · 4.82 KB CSS-in-JS engine · 170+ CSS props · 123 motion presets.', + creator: '@vitus_labs', + }, + robots: { + index: true, + follow: true, + googleBot: { + index: true, + follow: true, + 'max-image-preview': 'large', + 'max-snippet': -1, + }, + }, + alternates: { + canonical: SITE_URL, + }, +} + +export const viewport: Viewport = { + themeColor: [ + { media: '(prefers-color-scheme: light)', color: '#fdfbf5' }, + { media: '(prefers-color-scheme: dark)', color: '#0a0b0d' }, + ], + colorScheme: 'dark light', } export default function RootLayout({ children }: { children: ReactNode }) { diff --git a/src/app/opengraph-image.tsx b/src/app/opengraph-image.tsx new file mode 100644 index 0000000..0b57092 --- /dev/null +++ b/src/app/opengraph-image.tsx @@ -0,0 +1,279 @@ +import { ImageResponse } from 'next/og' + +/** + * Default Open Graph + Twitter card image for the site root. + * + * Generated at build time as a static PNG (1200×630) so social platforms + * cache a single asset. Matches the social-kit design: stacked-triangle + * mark + lockup top-left, big headline center, orbital motif on the right, + * chartreuse + cobalt aurora backdrop on dark. + */ + +// Static export: prerender at build time so a single PNG is emitted to /out +// and platforms cache one canonical asset. +export const dynamic = 'force-static' +export const alt = + 'Vitus Labs — Composable React engine. 15 packages, 4.82 KB CSS-in-JS engine, 170+ CSS props, 123 motion presets.' +export const size = { width: 1200, height: 630 } +export const contentType = 'image/png' + +const INK = '#0A0B0D' +const PAPER = '#ECEAE2' +const ACCENT = '#C8FF3A' +const COBALT = '#2F4DFF' +const MUTED = '#A8A69C' +const BORDER = '#2E3138' + +/** Stacked-triangle mark — three trapezoidal slabs over a unit canvas. */ +function Mark({ size: s = 36 }: { size?: number }) { + return ( + + + + + + ) +} + +/** Orbital motif — frozen frame of the hero visual, scaled for the right edge. */ +function Orbit() { + const rings = [ + { size: 480, opacity: 1, dashed: true }, + { size: 360, opacity: 0.5, dashed: false }, + { size: 260, opacity: 1, dashed: true }, + { size: 180, opacity: 0.5, dashed: false }, + ] + return ( +
+ {rings.map((r, i) => ( +
+ ))} + {/* Nodes anchored at the right edge of each ring */} +
+
+ {/* Center disc with mark */} +
+ +
+
+ ) +} + +export default async function OpenGraphImage() { + return new ImageResponse( +
+ {/* Background — radial chartreuse glow on the right + faint cobalt counter-note */} +
+ {/* Faint dot-grid */} +
+ + + + {/* Lockup */} +
+ +
+ vitus + + · + + labs +
+
+ + {/* Headline block */} +
+
+ — composable react engine +
+
+ Build, style + + & ship{' '} + + faster. + + +
+
+ 15 packages · 4.82 KB CSS-in-JS engine · 170+ CSS props · 123 motion + presets +
+
+ + {/* Footer */} +
+ vitus-labs.com + MIT · TYPESCRIPT-FIRST +
+
, + { + ...size, + }, + ) +} diff --git a/src/app/twitter-image.tsx b/src/app/twitter-image.tsx new file mode 100644 index 0000000..20a01b3 --- /dev/null +++ b/src/app/twitter-image.tsx @@ -0,0 +1,16 @@ +/** + * Twitter card image — same artwork as the OG card. Twitter's + * `summary_large_image` is also 1200×630, so we re-export the renderer. + * + * Next.js parses route-segment config statically per file, so the + * declarations have to be inlined here (not re-exported). + */ +import OpenGraphImage from './opengraph-image' + +export const dynamic = 'force-static' +export const alt = + 'Vitus Labs — Composable React engine. 15 packages, 4.82 KB CSS-in-JS engine, 170+ CSS props, 123 motion presets.' +export const size = { width: 1200, height: 630 } +export const contentType = 'image/png' + +export default OpenGraphImage