refactor: refresh marketing theme with brand blue (#573)

This commit is contained in:
shenlan 2025-10-18 18:07:36 +08:00 committed by GitHub
parent da9ca0858f
commit 9d69aab8b5
15 changed files with 278 additions and 208 deletions

View File

@ -54,17 +54,17 @@ export default function DocCollectionCard({ collection, meta }: DocCollectionCar
const tags = activeResource?.tags?.length ? activeResource.tags : collection.tags
return (
<article className="group relative flex h-full flex-col overflow-hidden rounded-3xl border border-gray-200 bg-white shadow-sm transition duration-200 hover:-translate-y-1 hover:shadow-lg">
<div className="relative h-32 w-full overflow-hidden bg-gradient-to-br from-purple-50 via-white to-purple-100">
<div className="absolute inset-0 flex flex-col justify-between p-4">
<article className="group relative flex h-full flex-col overflow-hidden rounded-2xl border border-brand-border bg-white shadow-[0_4px_20px_rgba(0,0,0,0.04)] transition duration-200 hover:-translate-y-1 hover:border-brand hover:shadow-[0_8px_28px_rgba(51,102,255,0.18)]">
<div className="relative h-28 w-full bg-brand/10">
<div className="absolute inset-0 flex flex-col justify-between p-4 text-xs text-brand-heading/70">
<div>
{meta && (
<span className="inline-flex items-center rounded-full bg-white/80 px-3 py-1 text-xs font-medium text-purple-700 shadow-sm">
<span className="inline-flex items-center rounded-full border border-brand-border bg-white/90 px-3 py-1 text-[11px] font-medium uppercase tracking-wide text-brand">
{meta}
</span>
)}
</div>
<div className="flex items-center justify-between text-xs text-purple-500">
<div className="flex items-center justify-between gap-2">
{updatedAt && (
<span suppressHydrationWarning>
Updated <ClientTime isoString={updatedAt} />
@ -75,19 +75,19 @@ export default function DocCollectionCard({ collection, meta }: DocCollectionCar
</div>
</div>
<div className="flex flex-1 flex-col gap-4 p-6">
<div className="flex flex-1 flex-col gap-4 p-6 text-brand-heading">
<div className="space-y-2">
<h2 className="text-lg font-semibold text-gray-900 transition group-hover:text-purple-700">{collection.title}</h2>
<p className="text-sm text-gray-600">{description}</p>
<h2 className="text-lg font-semibold text-brand-navy transition group-hover:text-brand">{collection.title}</h2>
<p className="text-sm text-brand-heading/80">{description}</p>
</div>
{versions.length > 0 && (
<label className="flex flex-col gap-1 text-xs font-semibold uppercase tracking-wide text-gray-500">
<label className="flex flex-col gap-1 text-xs font-semibold uppercase tracking-[0.2em] text-brand-heading/70">
<span>Version</span>
<select
value={selectedVersionId}
onChange={(event) => setSelectedVersionId(event.target.value)}
className="rounded-full border border-gray-300 px-3 py-1 text-sm font-medium text-gray-700 focus:border-purple-500 focus:outline-none focus:ring-1 focus:ring-purple-500"
className="rounded-full border border-brand-border px-3 py-1 text-sm font-medium text-brand-heading focus:border-brand focus:outline-none focus:ring-1 focus:ring-brand/30"
>
{versions.map((version) => (
<option key={version.id} value={version.id}>
@ -101,23 +101,23 @@ export default function DocCollectionCard({ collection, meta }: DocCollectionCar
{tags && tags.length > 0 && (
<div className="flex flex-wrap gap-2">
{tags.map((tag) => (
<span key={tag} className="rounded-full bg-purple-50 px-3 py-1 text-xs font-medium text-purple-700">
<span key={tag} className="rounded-full border border-brand-border bg-brand-surface px-3 py-1 text-xs font-medium text-brand-heading">
{tag}
</span>
))}
</div>
)}
<div className="mt-auto flex items-center justify-between text-sm font-medium text-purple-600">
<div className="flex flex-col">
<div className="mt-auto flex items-center justify-between text-sm font-medium text-brand">
<div className="flex flex-col text-brand-heading">
<span>Open reader</span>
{activeVersion && (
<span className="text-xs text-gray-500">{resolveVersionLabel(activeVersion)}</span>
<span className="text-xs text-brand-heading/70">{resolveVersionLabel(activeVersion)}</span>
)}
</div>
<Link
href={href}
className="inline-flex items-center gap-2 rounded-full border border-transparent bg-purple-50 px-3 py-1 text-purple-700 transition hover:border-purple-200 hover:bg-white"
className="inline-flex items-center gap-2 rounded-full border border-brand-border bg-brand-surface px-3 py-1 text-brand transition hover:border-brand hover:bg-white"
>
<span>Open</span>
<ArrowUpRight className="h-4 w-4 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />

View File

@ -6,6 +6,7 @@ import type { DocCollection } from './types'
import { getDocResources } from './resources.server'
import { isFeatureEnabled } from '@lib/featureToggles'
import DocCollectionCard from './DocCollectionCard'
import Footer from '@components/Footer'
function formatMeta(resource: DocCollection) {
const parts: string[] = []
@ -36,20 +37,20 @@ export default async function DocsHome() {
})
return (
<main className="px-4 py-8 md:px-8">
<div className="mx-auto flex max-w-6xl flex-col gap-8">
<header className="space-y-3">
<p className="text-sm font-semibold uppercase tracking-wide text-purple-600">Knowledge Base</p>
<h1 className="text-3xl font-bold md:text-4xl">Documentation Library</h1>
<p className="max-w-3xl text-sm text-gray-600 md:text-base">
Browse curated implementation guides, architecture notes, and runbooks from dl.svc.plus. Select a resource to
open the focused reading workspace.
<main className="bg-brand-surface px-6 py-12 sm:px-8">
<div className="mx-auto flex max-w-6xl flex-col gap-10">
<header className="space-y-4 text-brand-heading">
<p className="text-xs font-semibold uppercase tracking-[0.32em] text-brand">Knowledge Base</p>
<h1 className="text-[32px] font-bold text-brand md:text-[36px]">Documentation Library</h1>
<p className="max-w-3xl text-sm text-brand-heading/80 md:text-base">
Browse curated implementation guides, architecture notes, and runbooks from dl.svc.plus. Select a resource to open
the focused reading workspace.
</p>
</header>
<section>
{resources.length === 0 ? (
<div className="rounded-3xl border border-dashed border-gray-300 bg-white/70 p-10 text-center text-sm text-gray-500">
<div className="rounded-2xl border border-dashed border-brand-border bg-white p-10 text-center text-sm text-brand-heading/70">
Documentation resources are not available at the moment. Please check back later.
</div>
) : (
@ -62,6 +63,7 @@ export default async function DocsHome() {
)}
</section>
</div>
<Footer />
</main>
)
}

View File

@ -11,28 +11,28 @@
--app-shell-nav-offset: 5.5rem;
/* Light theme defaults */
--color-background: #f8fafc;
--color-background-muted: #f1f5f9;
--color-background: #f4f6fb;
--color-background-muted: #e7ecf6;
--color-surface: #ffffff;
--color-surface-elevated: rgba(255, 255, 255, 0.92);
--color-surface-translucent: rgba(255, 255, 255, 0.85);
--color-surface-muted: #f1f5f9;
--color-surface-hover: #f3f4f6;
--color-surface-border: #e2e8f0;
--color-surface-border-strong: #cbd5e1;
--color-text: #111827;
--color-heading: #0f172a;
--color-text-muted: #4b5563;
--color-text-subtle: #6b7280;
--color-text-inverse: #f8fafc;
--color-primary: #7c3aed;
--color-primary-hover: #6d28d9;
--color-primary-muted: #ede9fe;
--color-primary-border: #c4b5fd;
--color-primary-foreground: #f9fafb;
--color-accent: #6366f1;
--color-accent-muted: #e0e7ff;
--color-accent-foreground: #312e81;
--color-surface-elevated: rgba(255, 255, 255, 0.96);
--color-surface-translucent: rgba(255, 255, 255, 0.88);
--color-surface-muted: #f1f4fb;
--color-surface-hover: #f0f4ff;
--color-surface-border: #d6e0ff;
--color-surface-border-strong: #b4c5ff;
--color-text: #1e2e55;
--color-heading: #2e3a59;
--color-text-muted: #4a5672;
--color-text-subtle: #61708c;
--color-text-inverse: #f8fbff;
--color-primary: #3366ff;
--color-primary-hover: #4d7aff;
--color-primary-muted: #f0f4ff;
--color-primary-border: #d6e0ff;
--color-primary-foreground: #ffffff;
--color-accent: #254edb;
--color-accent-muted: #e3e9ff;
--color-accent-foreground: #162a6b;
--color-success: #16a34a;
--color-success-muted: #dcfce7;
--color-success-foreground: #166534;
@ -42,25 +42,25 @@
--color-danger: #ef4444;
--color-danger-muted: #fee2e2;
--color-danger-foreground: #7f1d1d;
--color-info: #2563eb;
--color-info-muted: #dbeafe;
--color-info-foreground: #1d4ed8;
--color-overlay: rgba(15, 23, 42, 0.45);
--color-ring: #c4b5fd;
--color-focus: rgba(124, 58, 237, 0.35);
--color-info: #3366ff;
--color-info-muted: #f0f4ff;
--color-info-foreground: #254edb;
--color-overlay: rgba(30, 46, 85, 0.45);
--color-ring: #d6e0ff;
--color-focus: rgba(51, 102, 255, 0.35);
--color-divider: rgba(15, 23, 42, 0.08);
--color-badge-surface: #e5e7eb;
--color-badge-muted: #f3f4f6;
--color-badge-foreground: #1f2937;
--gradient-app-from: #ede9fe;
--gradient-app-via: #f5f3ff;
--gradient-app-to: #e0f2fe;
--gradient-primary-from: #7c3aed;
--gradient-primary-to: #4c1d95;
--gradient-app-from: #f5f8ff;
--gradient-app-via: #eef3ff;
--gradient-app-to: #f4f9ff;
--gradient-primary-from: #3366ff;
--gradient-primary-to: #254edb;
--shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.04);
--shadow-md: 0 12px 32px rgba(15, 23, 42, 0.12);
--shadow-sm: 0 1px 3px rgba(30, 46, 85, 0.08), 0 1px 2px rgba(30, 46, 85, 0.04);
--shadow-md: 0 12px 32px rgba(30, 46, 85, 0.12);
--radius-lg: 1rem;
--radius-xl: 1.5rem;

View File

@ -1,30 +1,22 @@
import type { CommonHomeLayoutConfig } from '@templates/layouts/commonHome'
export const defaultHomeLayoutConfig: CommonHomeLayoutConfig = {
rootClassName:
'relative min-h-screen bg-gradient-to-b from-[#EAF3FF] to-[#CFE4FF] text-slate-900 antialiased',
rootClassName: 'relative min-h-screen bg-brand-surface text-brand-navy antialiased',
hero: {
sectionClassName: 'relative isolate overflow-hidden py-24',
overlays: [
'absolute inset-0 bg-gradient-to-b from-white/90 via-white/70 to-transparent',
'absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(59,130,246,0.18),_transparent_65%)]',
],
containerClassName: 'relative px-6 sm:px-8',
contentClassName: 'mx-auto max-w-7xl',
sectionClassName: 'relative isolate overflow-hidden py-24 sm:py-28',
overlays: ['absolute inset-0 bg-gradient-to-b from-white via-white/80 to-transparent'],
containerClassName: 'relative px-8',
contentClassName: 'mx-auto w-full max-w-6xl',
slot: {
key: 'ProductMatrix',
},
},
content: {
sectionClassName: 'relative isolate py-20',
overlays: [
'absolute inset-x-0 top-0 h-px bg-blue-100/70',
'absolute inset-0 bg-[radial-gradient(circle_at_bottom,_rgba(59,130,246,0.12),_transparent_70%)]',
],
containerClassName: 'relative px-6 sm:px-8',
contentClassName: 'mx-auto max-w-7xl',
gridClassName:
'grid gap-10 lg:grid-cols-[minmax(0,1fr)_360px] lg:items-start lg:gap-12',
sectionClassName: 'relative isolate py-20 sm:py-24',
overlays: ['absolute inset-x-0 top-0 h-px bg-brand-border/70'],
containerClassName: 'relative px-8',
contentClassName: 'mx-auto w-full max-w-6xl',
gridClassName: 'grid gap-8 lg:grid-cols-[minmax(0,1fr)_360px] lg:items-start lg:gap-12',
slots: [
{ key: 'ArticleFeed' },
{ key: 'Sidebar' },

View File

@ -5,23 +5,58 @@ import { translations } from '../i18n/translations'
export default function Footer() {
const { language } = useLanguage()
const t = translations[language].footerLinks
const [privacy, terms, contact] = t
return (
<footer className="bg-gray-100 text-gray-900 py-12 px-4">
<div className="max-w-7xl mx-auto flex flex-col md:flex-row justify-between items-center gap-4">
<div className="flex gap-6 text-sm">
<a href="#privacy" className="hover:text-purple-600">{t[0]}</a>
<a href="#terms" className="hover:text-purple-600">{t[1]}</a>
<a href="#contact" className="hover:text-purple-600">{t[2]}</a>
<footer className="bg-brand-navy text-white">
<div className="mx-auto flex w-full max-w-6xl flex-col gap-10 px-8 py-14">
<div className="flex flex-col gap-8 md:flex-row md:items-start md:justify-between">
<div className="space-y-3">
<p className="text-xs font-semibold uppercase tracking-[0.32em] text-brand-light/90">CloudNative Suite</p>
<p className="max-w-lg text-sm text-white/70">
Unified observability, DevOps, and AI workflows for enterprise cloud native teams.
</p>
<div className="flex flex-wrap gap-4 text-sm text-white/80">
<a href="#privacy" className="transition hover:text-brand-light">
{privacy}
</a>
<a href="#terms" className="transition hover:text-brand-light">
{terms}
</a>
<a href="#contact" className="transition hover:text-brand-light">
{contact}
</a>
</div>
</div>
<div className="flex flex-col gap-6 text-sm">
<div className="space-y-2">
<p className="text-sm font-semibold text-white">GitHub</p>
<a
href="https://github.com/CloudNativeSuite"
className="inline-flex items-center gap-2 text-white/80 transition hover:text-brand-light"
target="_blank"
rel="noreferrer"
>
<span>github.com/CloudNativeSuite</span>
</a>
</div>
<div className="space-y-2">
<p className="text-sm font-semibold text-white"></p>
<span className="text-white/80">CloudNative Suite </span>
</div>
<div className="space-y-2">
<p className="text-sm font-semibold text-white">Contact</p>
<a href="mailto:contact@cloudnativesuite.io" className="text-white/80 transition hover:text-brand-light">
contact@cloudnativesuite.io
</a>
</div>
</div>
</div>
<div className="flex gap-4 text-xl">
<a href="#" title="Twitter">🐦</a>
<a href="#" title="Email">📧</a>
<div className="flex flex-col gap-3 border-t border-white/10 pt-6 text-sm text-white/60 sm:flex-row sm:items-center sm:justify-between">
<span>© 2025 CloudNative Suite. All rights reserved.</span>
<span>Build with confidence in the cloud native era.</span>
</div>
</div>
<div className="text-center text-gray-500 text-sm mt-6">
© 2025 CloudNative Suite. All rights reserved.
</div>
</footer>
)
}

View File

@ -215,7 +215,7 @@ export default function Navbar() {
}
return (
<span className="rounded-full bg-purple-100 px-2 py-0.5 text-xs font-medium uppercase text-purple-600">
<span className="rounded-full bg-brand-surface px-2 py-0.5 text-xs font-medium uppercase text-brand">
{channelLabels.badges[previewChannel]}
</span>
)
@ -281,8 +281,8 @@ export default function Navbar() {
return (
<>
<nav ref={navRef} className="fixed top-0 z-50 w-full border-b border-gray-200 bg-white/80 backdrop-blur">
<div className="mx-auto flex max-w-7xl flex-col px-4">
<nav ref={navRef} className="fixed top-0 z-50 w-full border-b border-brand-border/60 bg-white/85 backdrop-blur">
<div className="mx-auto flex max-w-7xl flex-col px-6 sm:px-8">
<div className="flex items-center gap-6 py-4">
<div className="flex flex-1 items-center gap-8">
<Link href="/" className="flex items-center gap-2 text-xl font-semibold text-gray-900">
@ -296,18 +296,18 @@ export default function Navbar() {
/>
CloudNative Suite
</Link>
<div className="hidden lg:flex items-center gap-6 text-sm font-medium text-gray-700">
<div className="hidden lg:flex items-center gap-6 text-sm font-medium text-brand-heading">
{mainLinks.map((link) => (
<Link key={link.key} href={link.href} className="transition hover:text-purple-600">
<Link key={link.key} href={link.href} className="transition hover:text-brand">
{link.label}
</Link>
))}
{serviceItems.length > 0 ? (
<div className="group relative">
<button className="flex items-center gap-1 transition hover:text-purple-600">
<button className="flex items-center gap-1 transition hover:text-brand">
<span>{labels.moreServices}</span>
<svg
className="h-4 w-4 text-gray-500 transition group-hover:text-purple-600"
className="h-4 w-4 text-brand-heading/60 transition group-hover:text-brand"
fill="none"
stroke="currentColor"
strokeWidth={2}
@ -317,7 +317,7 @@ export default function Navbar() {
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
</svg>
</button>
<div className="pointer-events-none absolute left-0 top-full hidden min-w-[200px] translate-y-1 rounded-lg border border-gray-200 bg-white py-2 text-sm text-gray-700 opacity-0 shadow-lg transition-all duration-200 group-hover:pointer-events-auto group-hover:block group-hover:translate-y-2 group-hover:opacity-100">
<div className="pointer-events-none absolute left-0 top-full hidden min-w-[200px] translate-y-1 rounded-lg border border-brand-border bg-white py-2 text-sm text-brand-heading opacity-0 shadow-[0_4px_20px_rgba(0,0,0,0.08)] transition-all duration-200 group-hover:pointer-events-auto group-hover:block group-hover:translate-y-2 group-hover:opacity-100">
{serviceItems.map((child) => {
const isExternal = child.href.startsWith('http')
if (isExternal) {
@ -325,7 +325,7 @@ export default function Navbar() {
<a
key={child.key}
href={child.href}
className="flex items-center justify-between gap-2 px-4 py-2 transition hover:bg-gray-100 hover:text-purple-600"
className="flex items-center justify-between gap-2 px-4 py-2 transition hover:bg-brand-surface hover:text-brand"
target="_blank"
rel="noopener noreferrer"
>
@ -339,7 +339,7 @@ export default function Navbar() {
<Link
key={child.key}
href={child.href}
className="flex items-center justify-between gap-2 px-4 py-2 transition hover:bg-gray-100 hover:text-purple-600"
className="flex items-center justify-between gap-2 px-4 py-2 transition hover:bg-brand-surface hover:text-brand"
>
<span>{child.label}</span>
{getPreviewBadge(child.channels)}
@ -359,11 +359,11 @@ export default function Navbar() {
value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}
placeholder={labels.searchPlaceholder}
className="w-full rounded-full border border-gray-200 bg-gray-50 py-2 pl-4 pr-10 text-sm text-gray-900 transition focus:border-purple-500 focus:bg-white focus:outline-none focus:ring-2 focus:ring-purple-100"
className="w-full rounded-full border border-brand-border bg-brand-surface/60 py-2 pl-4 pr-10 text-sm text-brand-heading transition focus:border-brand focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand/20"
/>
<button
type="submit"
className="absolute right-2 top-1/2 flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-full bg-purple-600 text-white transition hover:bg-purple-500"
className="absolute right-2 top-1/2 flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-full bg-brand text-white transition hover:bg-brand-light"
aria-label="Ask AI"
>
<Search className="h-4 w-4" />
@ -374,22 +374,22 @@ export default function Navbar() {
<button
type="button"
onClick={() => setAccountMenuOpen((prev) => !prev)}
className="flex h-10 w-10 items-center justify-center rounded-full bg-purple-600 text-sm font-semibold text-white shadow-sm transition hover:bg-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-200 focus:ring-offset-2"
className="flex h-10 w-10 items-center justify-center rounded-full bg-brand text-sm font-semibold text-white shadow-[0_4px_12px_rgba(51,102,255,0.3)] transition hover:bg-brand-light focus:outline-none focus:ring-2 focus:ring-brand/30 focus:ring-offset-2"
aria-haspopup="menu"
aria-expanded={accountMenuOpen}
>
{accountInitial}
</button>
{accountMenuOpen ? (
<div className="absolute right-0 mt-2 w-56 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-lg">
<div className="border-b border-gray-100 bg-purple-50/60 px-4 py-3">
<p className="text-sm font-semibold text-gray-900">{user.username}</p>
<p className="text-xs text-gray-500">{user.email}</p>
<div className="absolute right-0 mt-2 w-56 overflow-hidden rounded-xl border border-brand-border bg-white shadow-[0_4px_20px_rgba(0,0,0,0.08)]">
<div className="border-b border-brand-border/60 bg-brand-surface px-4 py-3">
<p className="text-sm font-semibold text-brand-heading">{user.username}</p>
<p className="text-xs text-brand-heading/70">{user.email}</p>
</div>
<div className="py-1 text-sm text-gray-700">
<div className="py-1 text-sm text-brand-heading">
<Link
href="/panel"
className="block px-4 py-2 hover:bg-gray-100"
className="block px-4 py-2 transition hover:bg-brand-surface"
onClick={() => setAccountMenuOpen(false)}
>
{accountCopy.userCenter}
@ -406,14 +406,14 @@ export default function Navbar() {
) : null}
</div>
) : (
<div className="flex items-center gap-3 text-sm font-medium text-gray-700">
<Link href="/login" className="transition hover:text-purple-600">
<div className="flex items-center gap-3 text-sm font-medium text-brand-heading">
<Link href="/login" className="transition hover:text-brand">
{nav.account.login}
</Link>
<span className="h-3 w-px bg-gray-300" aria-hidden="true" />
<Link
href="/register"
className="rounded-full border border-purple-100 px-4 py-1.5 text-purple-600 transition hover:border-purple-200 hover:bg-purple-50"
className="rounded-full border border-brand-border px-4 py-1.5 text-brand transition hover:border-brand hover:bg-brand-surface"
>
{nav.account.register}
</Link>
@ -456,11 +456,11 @@ export default function Navbar() {
value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}
placeholder={labels.searchPlaceholder}
className="w-full rounded-full border border-gray-200 bg-gray-50 py-2 pl-4 pr-10 text-sm text-gray-900 transition focus:border-purple-500 focus:bg-white focus:outline-none focus:ring-2 focus:ring-purple-100"
className="w-full rounded-full border border-brand-border bg-brand-surface/60 py-2 pl-4 pr-10 text-sm text-brand-heading transition focus:border-brand focus:bg-white focus:outline-none focus:ring-2 focus:ring-brand/20"
/>
<button
type="submit"
className="absolute right-2 top-1/2 flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-full bg-purple-600 text-white transition hover:bg-purple-500"
className="absolute right-2 top-1/2 flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded-full bg-brand text-white transition hover:bg-brand-light"
aria-label="Ask AI"
>
<Search className="h-4 w-4" />
@ -532,26 +532,26 @@ export default function Navbar() {
) : null}
</div>
{user ? (
<div className="rounded-xl bg-purple-50 p-4 text-purple-700">
<div className="rounded-xl border border-brand-border bg-white p-4 text-brand-heading shadow-[0_4px_20px_rgba(0,0,0,0.04)]">
<div className="flex items-center gap-3">
<span className="flex h-10 w-10 items-center justify-center rounded-full bg-purple-600 text-sm font-semibold text-white">
<span className="flex h-10 w-10 items-center justify-center rounded-full bg-brand text-sm font-semibold text-white">
{accountInitial}
</span>
<div>
<p className="text-sm font-semibold">{user.username}</p>
<p className="text-xs text-purple-300">{user.email}</p>
<p className="text-xs text-brand-heading/60">{user.email}</p>
</div>
</div>
<Link
href="/panel"
className="mt-3 inline-flex items-center justify-center rounded-lg bg-white/80 px-3 py-1.5 text-xs font-semibold text-purple-600 transition hover:bg-white"
className="mt-3 inline-flex items-center justify-center rounded-lg border border-brand-border bg-white px-3 py-1.5 text-xs font-semibold text-brand transition hover:border-brand hover:text-brand-light"
onClick={() => setMenuOpen(false)}
>
{accountCopy.userCenter}
</Link>
<Link
href="/logout"
className="mt-3 inline-flex items-center justify-center rounded-lg border border-purple-200 px-3 py-1.5 text-xs font-semibold text-purple-600 transition hover:border-purple-300 hover:bg-purple-100 focus:outline-none focus:ring-2 focus:ring-purple-200 focus:ring-offset-2"
className="mt-3 inline-flex items-center justify-center rounded-lg border border-brand-border px-3 py-1.5 text-xs font-semibold text-brand transition hover:border-brand hover:bg-brand-surface focus:outline-none focus:ring-2 focus:ring-brand/30 focus:ring-offset-2"
onClick={() => setMenuOpen(false)}
>
{accountCopy.logout}
@ -565,7 +565,7 @@ export default function Navbar() {
<span className="h-3 w-px bg-gray-300" aria-hidden="true" />
<Link
href="/register"
className="rounded-full border border-purple-100 px-4 py-1.5 text-purple-600 transition hover:border-purple-200 hover:bg-purple-50"
className="rounded-full border border-brand-border px-4 py-1.5 text-brand transition hover:border-brand hover:bg-brand-surface"
onClick={() => setMenuOpen(false)}
>
{nav.account.register}

View File

@ -48,52 +48,55 @@ export default function ArticleFeedClient({ posts }: ArticleFeedClientProps) {
)
return (
<section className="space-y-8 text-slate-900">
<header className="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
<div className="space-y-1">
<p className="text-sm font-semibold uppercase tracking-[0.3em] text-blue-600">{articleFeed.eyebrow}</p>
<h2 className="text-2xl font-semibold text-slate-900 sm:text-3xl">{articleFeed.title}</h2>
<section className="space-y-8 text-brand-heading">
<header className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<div className="space-y-2">
<p className="text-xs font-semibold uppercase tracking-[0.32em] text-brand">{articleFeed.eyebrow}</p>
<h2 className="text-2xl font-semibold text-brand-navy sm:text-[26px]">{articleFeed.title}</h2>
</div>
<Link href="/docs" className="text-sm font-medium text-blue-600 transition hover:text-blue-700">
<Link
href="/docs"
className="text-sm font-medium text-brand transition hover:text-brand-light"
>
{articleFeed.viewAll}
</Link>
</header>
<div className="space-y-8">
{!mappedPosts.length ? (
<p className="rounded-3xl border border-dashed border-blue-200/70 bg-white/80 p-8 text-center text-sm text-slate-700">
<p className="rounded-2xl border border-dashed border-brand-border bg-white p-8 text-center text-sm text-brand-heading/70">
{articleFeed.empty}
</p>
) : null}
{mappedPosts.map((post) => (
<article
key={post.slug}
className="group rounded-3xl border border-blue-100 bg-white p-6 text-slate-800 shadow-lg shadow-blue-100/60 transition hover:-translate-y-1 hover:border-blue-200 hover:bg-blue-50/70 hover:shadow-blue-200 sm:p-8"
className="group rounded-2xl border border-brand-border bg-white p-6 text-brand-heading shadow-[0_4px_20px_rgba(0,0,0,0.04)] transition hover:-translate-y-1 hover:border-brand hover:shadow-[0_6px_24px_rgba(51,102,255,0.2)] sm:p-8"
>
<div className="flex flex-wrap items-center gap-2 text-xs text-slate-500 sm:text-sm">
<div className="flex flex-wrap items-center gap-2 text-xs text-brand-heading/60 sm:text-sm">
{post.formattedDate ? <span>{post.formattedDate}</span> : null}
{post.author ? (
<span className="flex items-center gap-2">
<span className="hidden h-1 w-1 rounded-full bg-slate-300 sm:inline" aria-hidden />
<span className="hidden h-1 w-1 rounded-full bg-brand-border sm:inline" aria-hidden />
<span>{post.author}</span>
</span>
) : null}
{post.readingTime ? (
<span className="flex items-center gap-2">
<span className="hidden h-1 w-1 rounded-full bg-slate-300 sm:inline" aria-hidden />
<span className="hidden h-1 w-1 rounded-full bg-brand-border sm:inline" aria-hidden />
<span>{post.readingTime}</span>
</span>
) : null}
</div>
<h3 className="mt-4 text-xl font-semibold text-slate-900 transition group-hover:text-blue-700 sm:text-2xl">
<h3 className="mt-4 text-lg font-medium text-brand-heading transition group-hover:text-brand sm:text-xl">
{post.title}
</h3>
{post.excerpt ? <p className="mt-3 text-sm text-slate-700 sm:text-base">{post.excerpt}</p> : null}
{post.excerpt ? <p className="mt-3 text-sm text-brand-heading/80 sm:text-base">{post.excerpt}</p> : null}
{post.tags.length ? (
<div className="mt-5 flex flex-wrap gap-2">
{post.tags.map((tag) => (
<span
key={tag}
className="inline-flex items-center rounded-full bg-blue-50 px-3 py-1 text-xs font-medium text-slate-700"
className="inline-flex items-center rounded-full border border-brand-border bg-brand-surface px-3 py-1 text-xs font-medium text-brand-heading"
>
#{tag}
</span>

View File

@ -103,7 +103,7 @@ function QrPreview({ item, qrAltSuffix }: QrPreviewProps) {
return (
<div className="flex flex-col gap-3">
<div className="relative overflow-hidden rounded-2xl border border-blue-100 bg-white p-3 shadow-inner shadow-blue-100/60">
<div className="relative overflow-hidden rounded-2xl border border-brand-border bg-white p-3 shadow-[0_4px_20px_rgba(0,0,0,0.04)]">
<div className="relative aspect-square w-full">
{item.qrImage ? (
<Image
@ -124,15 +124,15 @@ function QrPreview({ item, qrAltSuffix }: QrPreviewProps) {
aria-hidden
>
{pattern!.flat().map((isFilled, index) => (
<span key={`${item.slug}-${index}`} className={`block ${isFilled ? 'bg-slate-900' : 'bg-slate-50'}`} />
<span key={`${item.slug}-${index}`} className={`block ${isFilled ? 'bg-brand-navy' : 'bg-brand-surface'}`} />
))}
</div>
)}
</div>
</div>
<div className="space-y-1">
<p className="text-sm font-semibold text-slate-900">{item.title}</p>
{item.description ? <p className="text-xs text-slate-600">{item.description}</p> : null}
<p className="text-sm font-semibold text-brand-navy">{item.title}</p>
{item.description ? <p className="text-xs text-brand-heading/70">{item.description}</p> : null}
</div>
</div>
)
@ -141,23 +141,23 @@ function QrPreview({ item, qrAltSuffix }: QrPreviewProps) {
function InfoCard({ item }: { item: ContactItemContent }) {
const Icon = getIcon(item.icon)
return (
<div className="flex items-start gap-3 rounded-2xl border border-blue-100 bg-blue-50/80 p-4">
<div className="mt-1 rounded-full bg-blue-100 p-2 text-blue-600">
<div className="flex items-start gap-3 rounded-2xl border border-brand-border bg-white p-4 shadow-[0_4px_20px_rgba(0,0,0,0.04)]">
<div className="mt-1 rounded-full bg-brand-surface p-2 text-brand">
<Icon className="h-5 w-5" aria-hidden />
</div>
<div className="flex-1">
<p className="text-sm font-semibold text-slate-900">{item.title}</p>
{item.description ? <p className="mt-1 text-xs text-slate-600">{item.description}</p> : null}
<p className="text-sm font-semibold text-brand-navy">{item.title}</p>
{item.description ? <p className="mt-1 text-xs text-brand-heading/70">{item.description}</p> : null}
{item.bodyHtml ? (
<div
className="prose prose-sm mt-2 max-w-none text-slate-700"
className="prose prose-sm mt-2 max-w-none text-brand-heading/80 [&_a]:text-brand [&_a]:no-underline hover:[&_a]:underline"
dangerouslySetInnerHTML={{ __html: item.bodyHtml }}
/>
) : null}
{item.ctaLabel && item.ctaHref ? (
<Link
href={item.ctaHref}
className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-blue-600 transition hover:text-blue-700"
className="mt-3 inline-flex items-center gap-1 text-sm font-semibold text-brand transition hover:text-brand-light"
>
{item.ctaLabel}
<ChevronRight className="h-4 w-4" aria-hidden />
@ -208,7 +208,7 @@ export default function ContactPanelClient({ panel, className }: ContactPanelCli
<button
type="button"
onClick={() => setCollapsed(false)}
className="group inline-flex items-center gap-2 rounded-full border border-blue-300 bg-white px-4 py-2 text-sm font-semibold text-blue-700 shadow-sm shadow-blue-200/70"
className="group inline-flex items-center gap-2 rounded-full border border-brand-border bg-white px-4 py-2 text-sm font-semibold text-brand shadow-[0_4px_20px_rgba(0,0,0,0.04)] transition hover:border-brand hover:text-brand-light"
aria-label={contactMarketing.expandLabel}
>
<span>{contactMarketing.buttonLabel}</span>
@ -216,19 +216,19 @@ export default function ContactPanelClient({ panel, className }: ContactPanelCli
</button>
</div>
) : (
<section className="relative flex max-h-full min-h-0 flex-col overflow-hidden rounded-3xl border border-blue-100 bg-gradient-to-b from-white via-white to-blue-50 shadow-xl shadow-blue-200/60">
<div className="absolute inset-x-0 top-0 h-1 bg-gradient-to-r from-blue-400 via-blue-500 to-indigo-400" aria-hidden />
<div className="flex items-start justify-between gap-3 px-5 pt-5">
<section className="relative flex max-h-full min-h-0 flex-col overflow-hidden rounded-2xl border border-brand-border bg-white shadow-[0_4px_20px_rgba(0,0,0,0.04)]">
<div className="absolute inset-x-0 top-0 h-1 bg-brand" aria-hidden />
<div className="flex items-start justify-between gap-3 px-6 pt-6">
<div>
<p className="text-sm font-semibold uppercase tracking-[0.3em] text-blue-600">{localizedPanel.title}</p>
<p className="text-xs font-semibold uppercase tracking-[0.32em] text-brand">{localizedPanel.title}</p>
{localizedPanel.subtitle ? (
<p className="mt-1 text-xs text-slate-600">{localizedPanel.subtitle}</p>
<p className="mt-1 text-xs text-brand-heading/70">{localizedPanel.subtitle}</p>
) : null}
</div>
<button
type="button"
onClick={() => setCollapsed(true)}
className="rounded-full border border-blue-100 bg-white/80 p-1 text-slate-400 transition hover:text-slate-600"
className="rounded-full border border-brand-border bg-white p-1 text-brand-heading/60 transition hover:border-brand hover:text-brand"
aria-label={contactMarketing.collapseLabel}
>
<X className="h-4 w-4" aria-hidden />
@ -237,11 +237,11 @@ export default function ContactPanelClient({ panel, className }: ContactPanelCli
<div className="flex-1 overflow-y-auto">
{localizedPanel.bodyHtml ? (
<div
className="prose prose-sm px-5 pt-3 text-slate-700"
className="prose prose-sm px-6 pt-4 text-brand-heading/80"
dangerouslySetInnerHTML={{ __html: localizedPanel.bodyHtml }}
/>
) : null}
<div className="grid gap-4 px-5 pb-5 pt-4 sm:grid-cols-2">
<div className="grid gap-4 px-6 pb-6 pt-4 sm:grid-cols-2">
{localizedPanel.items.map((item) => {
if (item.type === 'qr') {
return (

View File

@ -11,9 +11,9 @@ export default async function ProductMatrix() {
}
return (
<div className="grid gap-10 lg:grid-cols-[minmax(0,1fr)_360px] lg:items-start lg:gap-12">
<div className="grid gap-8 lg:grid-cols-[minmax(0,1fr)_360px] lg:items-start lg:gap-12">
<ProductMatrixClient solutions={solutions} />
<ContactPanel className="w-full lg:sticky lg:top-16 lg:h-fit lg:w-[360px] lg:self-start" />
<ContactPanel className="w-full lg:sticky lg:top-0 lg:h-fit lg:w-[360px] lg:self-start" />
</div>
)
}

View File

@ -8,9 +8,6 @@ import type { HeroSolution } from '@cms/content'
import { useLanguage } from '@i18n/LanguageProvider'
import { translations } from '@i18n/translations'
const heroBackgroundOverlay =
'absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top,_rgba(59,130,246,0.18),_transparent_70%)]'
type ProductMatrixClientProps = {
solutions: HeroSolution[]
}
@ -79,39 +76,37 @@ export default function ProductMatrixClient({ solutions }: ProductMatrixClientPr
].filter(Boolean) as Array<{ label: string; href: string; variant: 'primary' | 'secondary' | 'ghost' }>
return (
<section className="space-y-10">
<div className="relative overflow-hidden rounded-[2.5rem] border border-blue-100 bg-white p-8 text-slate-900 shadow-2xl shadow-blue-200/60 lg:p-12">
<div className={heroBackgroundOverlay} aria-hidden />
<div className="absolute inset-0 -z-10 bg-[linear-gradient(135deg,_rgba(234,243,255,0.95),_rgba(207,228,255,0.65))]" aria-hidden />
<div className="relative grid gap-10 lg:grid-cols-[minmax(0,1.45fr)_minmax(0,1fr)]">
<div className="space-y-6">
<header className="space-y-3">
<span className="inline-flex items-center rounded-full border border-blue-200 bg-blue-50 px-4 py-1 text-xs font-semibold uppercase tracking-[0.45em] text-blue-700">
<section className="space-y-8">
<div className="rounded-2xl border border-brand-border bg-white p-8 text-brand-heading shadow-[0_4px_20px_rgba(0,0,0,0.04)] sm:p-10 lg:p-12">
<div className="grid gap-8 lg:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)] lg:gap-10">
<div className="space-y-8">
<header className="space-y-4">
<span className="inline-flex items-center rounded-full border border-brand-border bg-brand-surface px-4 py-1 text-xs font-semibold uppercase tracking-[0.4em] text-brand">
{productMatrix.badge}
</span>
<h1 className="text-3xl font-extrabold leading-tight text-slate-900 sm:text-4xl lg:text-5xl">
<h1 className="text-[30px] font-bold leading-tight text-brand sm:text-[34px] md:text-[36px]">
{productMatrix.title}
</h1>
<p className="text-sm text-slate-700 sm:text-base lg:text-lg">
<p className="text-sm text-brand-heading/80 sm:text-base lg:text-lg">
{productMatrix.description}
</p>
</header>
<ul className="grid gap-3 text-sm text-slate-700 sm:grid-cols-2 lg:grid-cols-3">
<ul className="grid gap-3 text-sm text-brand-heading/80 sm:grid-cols-2 lg:grid-cols-3">
{overviewHighlights.map((highlight) => (
<li
key={highlight}
className="flex items-start gap-3 rounded-2xl border border-blue-100 bg-blue-50/60 p-3"
className="flex items-start gap-3 rounded-2xl border border-brand-border bg-white p-3 shadow-[0_4px_20px_rgba(0,0,0,0.04)]"
>
<span className="mt-1 h-2 w-2 flex-shrink-0 rounded-full bg-blue-500" aria-hidden />
<span className="mt-1 h-2 w-2 flex-shrink-0 rounded-full bg-brand" aria-hidden />
<span>{highlight}</span>
</li>
))}
</ul>
<div className="rounded-3xl border border-blue-100 bg-blue-50/80 p-3 sm:p-4">
<p className="text-xs font-semibold uppercase tracking-[0.3em] text-blue-700">
<div className="rounded-2xl border border-brand-border bg-brand-surface p-4">
<p className="text-xs font-semibold uppercase tracking-[0.3em] text-brand">
{productMatrix.topicsLabel}
</p>
<div className="mt-3 flex flex-wrap gap-2">
<div className="mt-4 flex flex-wrap gap-3">
{localizedSolutions.map((solution, index) => {
const isActive = index === activeIndex
return (
@ -120,17 +115,17 @@ export default function ProductMatrixClient({ solutions }: ProductMatrixClientPr
type="button"
onClick={() => setActiveIndex(index)}
className={clsx(
'flex min-w-[9rem] flex-1 items-center justify-between rounded-2xl border px-4 py-3 text-left text-sm font-semibold transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-300',
'flex min-w-[9rem] flex-1 items-center justify-between rounded-2xl border px-4 py-3 text-left text-sm font-semibold transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand/40',
isActive
? 'border-blue-400 bg-blue-100 text-slate-900 shadow-lg shadow-blue-300/40'
: 'border-blue-100 bg-white text-slate-700 hover:border-blue-200 hover:bg-blue-50',
? 'border-brand bg-brand/10 text-brand-heading shadow-[0_4px_20px_rgba(0,0,0,0.04)]'
: 'border-brand-border bg-white text-brand-heading/80 hover:border-brand hover:bg-brand-surface',
)}
>
<span className="flex-1">{solution.title}</span>
<span className="flex-1 text-brand-heading">{solution.title}</span>
<span
className={clsx(
'ml-2 text-xs font-medium transition',
isActive ? 'text-slate-800/80' : 'text-slate-500',
isActive ? 'text-brand-heading/70' : 'text-brand-heading/60',
)}
>
{solution.tagline}
@ -141,33 +136,36 @@ export default function ProductMatrixClient({ solutions }: ProductMatrixClientPr
</div>
</div>
</div>
<div className="flex flex-col gap-6 rounded-3xl border border-blue-100 bg-white/90 p-6 shadow-inner shadow-blue-100/80 backdrop-blur-sm lg:p-8">
<div className="flex flex-col gap-6 rounded-2xl border border-brand-border bg-white p-6 shadow-[0_4px_20px_rgba(0,0,0,0.04)] lg:p-8">
<div className="space-y-4">
{activeSolution.tagline ? (
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-blue-700">
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-brand">
{activeSolution.tagline}
</p>
) : null}
<h2 className="text-2xl font-semibold text-slate-900 sm:text-3xl">{activeSolution.title}</h2>
<h2 className="text-2xl font-semibold text-brand-navy sm:text-[26px]">{activeSolution.title}</h2>
{activeSolution.description ? (
<p className="text-sm text-slate-700 sm:text-base">{activeSolution.description}</p>
<p className="text-sm text-brand-heading/80 sm:text-base">{activeSolution.description}</p>
) : null}
{activeSolution.bodyHtml ? (
<div
className="prose max-w-none text-sm text-slate-800 [&_strong]:text-slate-900"
className="prose max-w-none text-sm text-brand-heading/90 [&_strong]:text-brand-navy"
dangerouslySetInnerHTML={{ __html: activeSolution.bodyHtml }}
/>
) : null}
</div>
{activeSolution.features.length ? (
<div className="space-y-3">
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-blue-700">
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-brand">
{productMatrix.capabilitiesLabel}
</p>
<ul className="grid gap-2 text-sm text-slate-700 sm:grid-cols-2">
<ul className="grid gap-2 text-sm text-brand-heading/80 sm:grid-cols-2">
{activeSolution.features.map((feature) => (
<li key={feature} className="flex items-start gap-3 rounded-2xl border border-blue-100 bg-blue-50/60 p-3">
<span className="mt-1 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-blue-500" aria-hidden />
<li
key={feature}
className="flex items-start gap-3 rounded-2xl border border-brand-border bg-white p-3 shadow-[0_4px_20px_rgba(0,0,0,0.04)]"
>
<span className="mt-1 h-1.5 w-1.5 flex-shrink-0 rounded-full bg-brand" aria-hidden />
<span>{feature}</span>
</li>
))}
@ -182,12 +180,12 @@ export default function ProductMatrixClient({ solutions }: ProductMatrixClientPr
prefetch={false}
href={href}
className={clsx(
'inline-flex items-center justify-center rounded-full px-5 py-2 text-sm font-semibold transition',
'inline-flex items-center justify-center rounded-full px-5 py-2 text-sm font-semibold transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand/30',
variant === 'primary'
? 'bg-blue-600 text-white shadow-lg shadow-blue-300/50 hover:bg-blue-700'
? 'bg-brand text-white shadow-[0_4px_20px_rgba(51,102,255,0.25)] hover:bg-brand-light'
: variant === 'secondary'
? 'border border-blue-200 text-blue-700 hover:border-blue-300 hover:bg-blue-50'
: 'border border-blue-100 text-slate-700 hover:border-blue-200 hover:bg-blue-50/80',
? 'border border-brand-border text-brand hover:border-brand hover:bg-brand-surface'
: 'border border-brand-border text-brand-heading/80 hover:border-brand hover:bg-brand-surface',
)}
>
{label}

View File

@ -10,7 +10,7 @@ export default async function Sidebar() {
}
return (
<aside className="w-full space-y-6 rounded-3xl border border-blue-100 bg-white/95 p-6 text-slate-900 shadow-xl shadow-blue-200/60 backdrop-blur lg:sticky lg:top-16 lg:h-fit lg:w-[360px]">
<aside className="w-full space-y-6 rounded-2xl border border-brand-border bg-white p-6 text-brand-heading shadow-[0_4px_20px_rgba(0,0,0,0.04)] lg:sticky lg:top-0 lg:h-fit lg:w-[360px]">
{sections.map((section) => (
<SidebarCard key={section.slug} section={section} />
))}

View File

@ -36,13 +36,13 @@ export default function SidebarCard({ section }: SidebarCardProps) {
const hasTagsLayout = localizedSection.layout === 'tags' && localizedSection.tags.length > 0
return (
<section className="rounded-2xl border border-blue-100 bg-white/95 p-5 shadow-md shadow-blue-200/50">
<section className="rounded-2xl border border-brand-border bg-white p-5 text-brand-heading shadow-[0_4px_20px_rgba(0,0,0,0.04)]">
<header className="mb-4 flex items-center justify-between gap-3">
<h3 className="text-lg font-semibold text-slate-900">{localizedSection.title}</h3>
<h3 className="text-lg font-medium text-brand-heading">{localizedSection.title}</h3>
{isValidCta(localizedSection) ? (
<Link
href={localizedSection.ctaHref}
className="text-sm font-medium text-blue-600 transition hover:text-blue-700"
className="text-sm font-medium text-brand transition hover:text-brand-light"
>
{localizedSection.ctaLabel}
</Link>
@ -53,7 +53,7 @@ export default function SidebarCard({ section }: SidebarCardProps) {
{localizedSection.tags.map((tag) => (
<span
key={tag}
className="inline-flex items-center rounded-full bg-blue-50 px-3 py-1 text-xs font-medium text-slate-700"
className="inline-flex items-center rounded-full border border-brand-border bg-brand-surface px-3 py-1 text-xs font-medium text-brand-heading"
>
#{tag}
</span>
@ -61,7 +61,7 @@ export default function SidebarCard({ section }: SidebarCardProps) {
</div>
) : (
<div
className="prose prose-sm max-w-none text-slate-700 [&_a]:text-blue-600 [&_a]:no-underline hover:[&_a]:underline"
className="prose prose-sm max-w-none text-brand-heading/80 [&_a]:text-brand [&_a]:no-underline hover:[&_a]:underline"
dangerouslySetInnerHTML={{ __html: localizedSection.bodyHtml }}
/>
)}

View File

@ -1,4 +1,5 @@
import { Fragment, type ComponentType } from 'react'
import Footer from '@components/Footer'
import clsx from 'clsx'
import type { HomePageSlotKey, HomePageTemplateSlots, TemplateComponent } from '../types'
@ -103,6 +104,7 @@ export function createCommonHomeTemplate(
</div>
</div>
</section>
<Footer />
</main>
)
}

View File

@ -8,6 +8,17 @@ module.exports = {
sans: ['var(--font-geist-sans)', 'sans-serif'],
mono: ['var(--font-geist-mono)', 'monospace'],
},
colors: {
brand: {
DEFAULT: '#3366FF',
light: '#4D7AFF',
dark: '#254EDB',
surface: '#F5F8FF',
border: '#D6E0FF',
navy: '#1E2E55',
heading: '#2E3A59',
},
},
},
},
plugins: [require('@tailwindcss/typography')],

View File

@ -1,6 +1,7 @@
'use client'
import MarkdownSection from '../components/MarkdownSection'
import Footer from '@components/Footer'
import { useLanguage, type Language } from '../../i18n/LanguageProvider'
const SECTION_PATHS: Record<Language, {
@ -36,29 +37,55 @@ export default function MarkdownHomepage() {
const sections = SECTION_PATHS[language] ?? SECTION_PATHS[DEFAULT_LANGUAGE]
return (
<main className="flex flex-col gap-16 bg-white py-16">
<header className="bg-slate-950 py-16 text-white">
<div className="mx-auto flex max-w-4xl flex-col gap-6 px-6">
<main className="flex flex-col bg-brand-surface text-brand-heading">
<header className="bg-brand py-16 text-white">
<div className="mx-auto flex w-full max-w-5xl flex-col gap-6 px-8">
<MarkdownSection
src={sections.operations}
headingLevel="h1"
className="flex flex-col gap-4"
headingClassName="text-3xl font-semibold text-white sm:text-4xl"
contentClassName="prose-invert prose-headings:text-white prose-strong:text-white text-slate-100"
headingClassName="text-[36px] font-bold text-white"
contentClassName="prose-invert prose-headings:text-white prose-strong:text-white text-white/90"
/>
</div>
</header>
<section className="mx-auto flex w-full max-w-6xl flex-col gap-12 px-6">
<MarkdownSection src={sections.productSpotlight} />
<div className="grid gap-12 lg:grid-cols-[2fr_1fr]">
<MarkdownSection src={sections.news} />
<section className="mx-auto flex w-full max-w-6xl flex-col gap-12 px-8 py-16">
<MarkdownSection
src={sections.productSpotlight}
className="rounded-2xl border border-brand-border bg-white p-8 shadow-[0_4px_20px_rgba(0,0,0,0.04)]"
headingClassName="text-2xl font-semibold text-brand-navy"
contentClassName="prose prose-slate mt-6 max-w-none text-brand-heading/80"
/>
<div className="grid gap-12 lg:grid-cols-[minmax(0,2fr)_360px]">
<MarkdownSection
src={sections.news}
className="rounded-2xl border border-brand-border bg-white p-8 shadow-[0_4px_20px_rgba(0,0,0,0.04)]"
headingClassName="text-2xl font-semibold text-brand-navy"
contentClassName="prose prose-slate mt-6 max-w-none text-brand-heading/80"
/>
<div className="flex flex-col gap-12">
<MarkdownSection src={sections.support} />
<MarkdownSection src={sections.resources} />
<MarkdownSection
src={sections.support}
className="rounded-2xl border border-brand-border bg-white p-8 shadow-[0_4px_20px_rgba(0,0,0,0.04)]"
headingClassName="text-2xl font-semibold text-brand-navy"
contentClassName="prose prose-slate mt-6 max-w-none text-brand-heading/80"
/>
<MarkdownSection
src={sections.resources}
className="rounded-2xl border border-brand-border bg-white p-8 shadow-[0_4px_20px_rgba(0,0,0,0.04)]"
headingClassName="text-2xl font-semibold text-brand-navy"
contentClassName="prose prose-slate mt-6 max-w-none text-brand-heading/80"
/>
</div>
</div>
<MarkdownSection src={sections.community} />
<MarkdownSection
src={sections.community}
className="rounded-2xl border border-brand-border bg-white p-8 shadow-[0_4px_20px_rgba(0,0,0,0.04)]"
headingClassName="text-2xl font-semibold text-brand-navy"
contentClassName="prose prose-slate mt-6 max-w-none text-brand-heading/80"
/>
</section>
<Footer />
</main>
)
}