diff --git a/website-next/app/(content)/help/page.tsx b/website-next/app/(content)/help/page.tsx index 94f8259fce9..019da51e56c 100644 --- a/website-next/app/(content)/help/page.tsx +++ b/website-next/app/(content)/help/page.tsx @@ -1,6 +1,14 @@ +import type { Metadata } from "next"; + import { type Plan, PlanGrid } from "@/src/components/PlanGrid"; import { Section } from "@/src/components/Section"; +export const metadata: Metadata = { + title: "Help", + description: + "Need urgent GraphQL help? Join the ChilliCream community Slack, book a session with an expert, or choose a support plan that fits your team.", +}; + const PLANS: Plan[] = [ { title: "Community", diff --git a/website-next/app/(content)/platform/analytics/page.tsx b/website-next/app/(content)/platform/analytics/page.tsx index b97455e2520..cf53433598c 100644 --- a/website-next/app/(content)/platform/analytics/page.tsx +++ b/website-next/app/(content)/platform/analytics/page.tsx @@ -1,3 +1,5 @@ +import type { Metadata } from "next"; + import { Picture } from "@/src/design-system/Picture"; import { ContentSection } from "@/src/components/ContentSection"; @@ -5,6 +7,12 @@ import { NextStepsSection } from "@/src/components/NextStepsSection"; import { PageHero } from "@/src/components/PageHero"; import { SolidButton } from "@/src/design-system/Button"; +export const metadata: Metadata = { + title: "Analytics", + description: + "Monitor your GraphQL APIs with ChilliCream analytics: real-time insights, throughput and latency metrics, and full OpenTelemetry trace exploration.", +}; + export default function AnalyticsPage() { return ( <> diff --git a/website-next/app/(content)/platform/continuous-integration/page.tsx b/website-next/app/(content)/platform/continuous-integration/page.tsx index b65ad7051ea..896f7761410 100644 --- a/website-next/app/(content)/platform/continuous-integration/page.tsx +++ b/website-next/app/(content)/platform/continuous-integration/page.tsx @@ -1,8 +1,16 @@ +import type { Metadata } from "next"; + import { ContentSection } from "@/src/components/ContentSection"; import { NextStepsSection } from "@/src/components/NextStepsSection"; import { PageHero } from "@/src/components/PageHero"; import { SolidButton } from "@/src/design-system/Button"; +export const metadata: Metadata = { + title: "Continuous Integration", + description: + "Ship GraphQL changes safely with ChilliCream: schema validation, a centralized registry, environment workflows, and CI/CD integration via the Nitro CLI.", +}; + export default function ContinuousIntegrationPage() { return ( <> diff --git a/website-next/app/(content)/platform/ecosystem/page.tsx b/website-next/app/(content)/platform/ecosystem/page.tsx index b3b1db131aa..a2ef620396b 100644 --- a/website-next/app/(content)/platform/ecosystem/page.tsx +++ b/website-next/app/(content)/platform/ecosystem/page.tsx @@ -1,8 +1,16 @@ +import type { Metadata } from "next"; + import { ContentSection } from "@/src/components/ContentSection"; import { NextStepsSection } from "@/src/components/NextStepsSection"; import { PageHero } from "@/src/components/PageHero"; import { Section } from "@/src/components/Section"; +export const metadata: Metadata = { + title: "Ecosystem", + description: + "Explore the ChilliCream ecosystem: GraphQL tooling with authentication flows, document sync, subscriptions, and a fast IDE built for your API journey.", +}; + interface FeatureCard { title: string; description: string; diff --git a/website-next/app/(content)/platform/page.tsx b/website-next/app/(content)/platform/page.tsx index 7faeaf34c40..d7fd278a36d 100644 --- a/website-next/app/(content)/platform/page.tsx +++ b/website-next/app/(content)/platform/page.tsx @@ -1,8 +1,15 @@ +import type { Metadata } from "next"; import Link from "next/link"; import { PageHero } from "@/src/components/PageHero"; import { Section } from "@/src/components/Section"; +export const metadata: Metadata = { + title: "Platform", + description: + "Discover the ChilliCream GraphQL Platform: one place for analytics, continuous integration, and a trusted ecosystem for every API in your organization.", +}; + const PLATFORM_SECTIONS = [ { href: "/platform/analytics", diff --git a/website-next/app/(content)/pricing/page.tsx b/website-next/app/(content)/pricing/page.tsx index 05d7200be67..3c46fa8fcff 100644 --- a/website-next/app/(content)/pricing/page.tsx +++ b/website-next/app/(content)/pricing/page.tsx @@ -1,5 +1,13 @@ +import type { Metadata } from "next"; + import { Typography } from "@/src/design-system/Typography"; +export const metadata: Metadata = { + title: "Pricing", + description: + "Explore pricing for the ChilliCream GraphQL Platform. Compare plans and find the right option for your team, from open source to enterprise.", +}; + export default function Page() { return TODO: Pricing page; } diff --git a/website-next/app/(content)/products/hotchocolate/page.tsx b/website-next/app/(content)/products/hotchocolate/page.tsx index e117811b563..ff75a7f2c8b 100644 --- a/website-next/app/(content)/products/hotchocolate/page.tsx +++ b/website-next/app/(content)/products/hotchocolate/page.tsx @@ -1,8 +1,16 @@ +import type { Metadata } from "next"; + import { ContentSection } from "@/src/components/ContentSection"; import { PageHero } from "@/src/components/PageHero"; import { Section } from "@/src/components/Section"; import { OutlineButton, SolidButton } from "@/src/design-system/Button"; +export const metadata: Metadata = { + title: "Hot Chocolate", + description: + "Hot Chocolate is the GraphQL server for .NET: build type-safe APIs with DataLoader batching, subscriptions, OpenTelemetry, and federation via Fusion.", +}; + const FEATURES = [ { title: "Compile-time Composition", diff --git a/website-next/app/(content)/products/nitro/page.tsx b/website-next/app/(content)/products/nitro/page.tsx index eab5ef37f2e..b43af199e92 100644 --- a/website-next/app/(content)/products/nitro/page.tsx +++ b/website-next/app/(content)/products/nitro/page.tsx @@ -1,8 +1,16 @@ +import type { Metadata } from "next"; + import { ContentSection } from "@/src/components/ContentSection"; import { PageHero } from "@/src/components/PageHero"; import { Section } from "@/src/components/Section"; import { OutlineButton, SolidButton } from "@/src/design-system/Button"; +export const metadata: Metadata = { + title: "Nitro", + description: + "Nitro is ChilliCream's GraphQL IDE: explore any GraphQL API with authentication flows, document sync, and subscriptions over SSE and WebSockets.", +}; + const FEATURES: { title: string; description: string }[] = [ { title: "Authentication Flows", diff --git a/website-next/app/(content)/products/strawberryshake/page.tsx b/website-next/app/(content)/products/strawberryshake/page.tsx index 359a45ec5bd..d63d5f358d2 100644 --- a/website-next/app/(content)/products/strawberryshake/page.tsx +++ b/website-next/app/(content)/products/strawberryshake/page.tsx @@ -1,8 +1,16 @@ +import type { Metadata } from "next"; + import { ContentSection } from "@/src/components/ContentSection"; import { PageHero } from "@/src/components/PageHero"; import { Section } from "@/src/components/Section"; import { OutlineButton, SolidButton } from "@/src/design-system/Button"; +export const metadata: Metadata = { + title: "Strawberry Shake", + description: + "Strawberry Shake is a strongly-typed GraphQL client for .NET with a reactive store, caching, subscriptions, and compile-time source generators.", +}; + const FEATURES = [ { title: "Strongly-typed Client", diff --git a/website-next/app/(content)/services/advisory/page.tsx b/website-next/app/(content)/services/advisory/page.tsx index af4c46aa209..c5df4828f19 100644 --- a/website-next/app/(content)/services/advisory/page.tsx +++ b/website-next/app/(content)/services/advisory/page.tsx @@ -1,6 +1,14 @@ +import type { Metadata } from "next"; + import { Offering } from "@/src/components/Offering"; import { PageHero } from "@/src/components/PageHero"; +export const metadata: Metadata = { + title: "Advisory", + description: + "Get quick access to ChilliCream's GraphQL experts: hourly consulting, architecture guidance, code reviews, and full contracting engagements.", +}; + interface InquiryPlan { title: string; description: string; diff --git a/website-next/app/(content)/services/page.tsx b/website-next/app/(content)/services/page.tsx index eb21c8d9237..02ee40fed19 100644 --- a/website-next/app/(content)/services/page.tsx +++ b/website-next/app/(content)/services/page.tsx @@ -1,8 +1,15 @@ +import type { Metadata } from "next"; import Link from "next/link"; import { PageHero } from "@/src/components/PageHero"; import { Section } from "@/src/components/Section"; +export const metadata: Metadata = { + title: "Services", + description: + "Work with ChilliCream's GraphQL experts: advisory and consulting, support plans with SLAs you can rely on, and focused training for your team.", +}; + const SERVICE_SECTIONS = [ { href: "/services/advisory", diff --git a/website-next/app/(content)/services/support/contact/page.tsx b/website-next/app/(content)/services/support/contact/page.tsx index 025884c5358..e5feae13013 100644 --- a/website-next/app/(content)/services/support/contact/page.tsx +++ b/website-next/app/(content)/services/support/contact/page.tsx @@ -1,6 +1,14 @@ +import type { Metadata } from "next"; + import { PageHero } from "@/src/components/PageHero"; import { ContactForm } from "./ContactForm"; +export const metadata: Metadata = { + title: "Contact Us", + description: + "Contact the ChilliCream team: tell us what you need, from support plans and demos to GraphQL consulting and training, and we will be in touch shortly.", +}; + export default function ContactPage() { return ( <> diff --git a/website-next/app/(content)/services/support/page.tsx b/website-next/app/(content)/services/support/page.tsx index d3c34934e03..7d84e627383 100644 --- a/website-next/app/(content)/services/support/page.tsx +++ b/website-next/app/(content)/services/support/page.tsx @@ -1,3 +1,5 @@ +import type { Metadata } from "next"; + import { type Plan, PlanGrid } from "@/src/components/PlanGrid"; import { PageHero } from "@/src/components/PageHero"; import { Section } from "@/src/components/Section"; @@ -11,6 +13,12 @@ import { } from "@/src/design-system/Table"; import { CheckIcon } from "@/src/components/CheckIcon"; +export const metadata: Metadata = { + title: "Support", + description: + "Get GraphQL support from ChilliCream experts: plans from a free community Slack to enterprise SLAs with dedicated account managers and phone support.", +}; + const SUPPORT_PLANS: Plan[] = [ { title: "Community", diff --git a/website-next/app/(content)/services/training/page.tsx b/website-next/app/(content)/services/training/page.tsx index ef24576ac70..990320b9880 100644 --- a/website-next/app/(content)/services/training/page.tsx +++ b/website-next/app/(content)/services/training/page.tsx @@ -1,7 +1,15 @@ +import type { Metadata } from "next"; + import { Offering } from "@/src/components/Offering"; import { PageHero } from "@/src/components/PageHero"; import { Section } from "@/src/components/Section"; +export const metadata: Metadata = { + title: "Training", + description: + "Train your team with ChilliCream: corporate GraphQL training and workshops covering Hot Chocolate, ASP.NET Core, React, and Relay, taught by experts.", +}; + interface CorporateService { kind: string; description: string; diff --git a/website-next/app/apple-icon.png b/website-next/app/apple-icon.png new file mode 100644 index 00000000000..d087583864c Binary files /dev/null and b/website-next/app/apple-icon.png differ diff --git a/website-next/app/blog/[...slug]/page.tsx b/website-next/app/blog/[...slug]/page.tsx index 709ef769ef8..2f381bcfd5a 100644 --- a/website-next/app/blog/[...slug]/page.tsx +++ b/website-next/app/blog/[...slug]/page.tsx @@ -95,11 +95,25 @@ export async function generateMetadata({ return { title, description, + ...(summary?.author + ? { authors: [{ name: summary.author, url: summary.authorUrl ?? undefined }] } + : {}), + alternates: { + canonical: summary?.href, + }, openGraph: { type: "article", title, description, images, + url: summary?.href, + publishedTime: summary?.date, + authors: summary?.authorUrl + ? [summary.authorUrl] + : summary?.author + ? [summary.author] + : undefined, + tags: summary && summary.tags.length > 0 ? summary.tags : undefined, }, twitter: { card: "summary_large_image", @@ -143,8 +157,40 @@ export default async function BlogSlugPage({ params }: PageProps) { const similar = current ? findSimilarPosts(current, summaries) : []; const featuredImage = current?.featuredImage ?? null; + const jsonLd = current + ? { + "@context": "https://schema.org", + "@type": "BlogPosting", + headline: current.title, + ...(current.description ? { description: current.description } : {}), + datePublished: current.date, + ...(current.featuredImage + ? { image: toAbsoluteUrl(current.featuredImage) } + : {}), + ...(current.author + ? { + author: { + "@type": "Person", + name: current.author, + ...(current.authorUrl ? { url: current.authorUrl } : {}), + }, + } + : {}), + mainEntityOfPage: toAbsoluteUrl(current.href), + } + : null; + return (
+ {jsonLd ? ( +