feat(dashboard-fresh): improve homepage and navbar
- Enhanced homepage visuals: larger headings, gradient theme, improved CTA animations, and better text readability - Added dynamic navbar offset using CSS variable (--app-shell-nav-offset) - Fixed language toggle and routing consistency - Introduced new components: Hero, CtaButtons, ShowcaseCarousel - Resolved Preact version mismatch and <Head> rendering issues
This commit is contained in:
parent
dd6bb68d25
commit
d37f2dab42
63
dashboard-fresh/components/home/CtaButtons.tsx
Normal file
63
dashboard-fresh/components/home/CtaButtons.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* CTA Buttons Component - Static Component
|
||||
*
|
||||
* Call-to-action buttons with enhanced hover effects and 2C brand styling
|
||||
*/
|
||||
|
||||
interface CtaButtonsProps {
|
||||
className?: string
|
||||
primaryText?: string
|
||||
primaryHref?: string
|
||||
secondaryText?: string
|
||||
secondaryHref?: string
|
||||
}
|
||||
|
||||
export default function CtaButtons({
|
||||
className = '',
|
||||
primaryText = '开始使用',
|
||||
primaryHref = '/register',
|
||||
secondaryText = '查看文档',
|
||||
secondaryHref = '/docs',
|
||||
}: CtaButtonsProps) {
|
||||
return (
|
||||
<div class={`flex flex-col items-center justify-center gap-4 sm:flex-row ${className}`}>
|
||||
{/* Primary CTA - Gradient button with hover animation */}
|
||||
<a
|
||||
href={primaryHref}
|
||||
class="group relative inline-flex items-center justify-center gap-2 overflow-hidden rounded-xl bg-gradient-to-r from-sky-600 to-indigo-600 px-8 py-4 font-semibold text-white shadow-lg transition-all hover:scale-105 hover:shadow-xl active:scale-100"
|
||||
>
|
||||
<span class="absolute inset-0 bg-gradient-to-r from-sky-700 to-indigo-700 opacity-0 transition-opacity group-hover:opacity-100" />
|
||||
<span class="relative">{primaryText}</span>
|
||||
<svg
|
||||
class="relative h-5 w-5 transition-transform group-hover:translate-x-1"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
{/* Secondary CTA - Outlined button with hover effects */}
|
||||
<a
|
||||
href={secondaryHref}
|
||||
class="group inline-flex items-center justify-center gap-2 rounded-xl border-2 border-slate-300 bg-white px-8 py-4 font-semibold text-slate-900 shadow-sm transition-all hover:border-sky-400 hover:bg-slate-50 hover:shadow-md active:scale-95"
|
||||
>
|
||||
<span>{secondaryText}</span>
|
||||
<svg
|
||||
class="h-5 w-5 transition-transform group-hover:translate-x-0.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
134
dashboard-fresh/components/home/Hero.tsx
Normal file
134
dashboard-fresh/components/home/Hero.tsx
Normal file
@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Hero Section - Static Component
|
||||
*
|
||||
* Improved design with:
|
||||
* - Enhanced typography (48-60px headings)
|
||||
* - High contrast (text-slate-900)
|
||||
* - Proper spacing (pt-16 md:pt-24, pb-20 md:pb-32)
|
||||
* - Text width constraints (max-w-[70ch])
|
||||
* - Gradient background with 2C brand atmosphere
|
||||
*/
|
||||
|
||||
interface HeroProps {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default function Hero({ className = '' }: HeroProps) {
|
||||
return (
|
||||
<section
|
||||
class={`relative overflow-hidden bg-gradient-to-br from-sky-50 via-indigo-50/30 to-white pt-16 pb-20 md:pt-24 md:pb-32 ${className}`}
|
||||
>
|
||||
{/* Decorative blur elements for 2C visual appeal */}
|
||||
<div class="pointer-events-none absolute inset-0 overflow-hidden">
|
||||
<div class="absolute -top-40 -right-40 h-80 w-80 rounded-full bg-sky-200/30 blur-3xl" />
|
||||
<div class="absolute top-1/2 -left-40 h-96 w-96 rounded-full bg-indigo-200/20 blur-3xl" />
|
||||
<div class="absolute bottom-0 right-1/4 h-64 w-64 rounded-full bg-purple-200/20 blur-3xl" />
|
||||
</div>
|
||||
|
||||
<div class="relative mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div class="mx-auto max-w-4xl text-center">
|
||||
{/* Badge */}
|
||||
<div class="mb-8 inline-flex items-center gap-2 rounded-full border border-sky-200 bg-white/80 px-4 py-2 shadow-sm backdrop-blur-sm transition-all hover:border-sky-300 hover:shadow-md">
|
||||
<span class="flex h-2 w-2">
|
||||
<span class="absolute inline-flex h-2 w-2 animate-ping rounded-full bg-sky-400 opacity-75" />
|
||||
<span class="relative inline-flex h-2 w-2 rounded-full bg-sky-500" />
|
||||
</span>
|
||||
<span class="text-sm font-semibold text-slate-900">
|
||||
全新升级 · 统一云原生控制平面
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Main Heading - Improved typography */}
|
||||
<h1 class="mb-6 text-4xl font-extrabold tracking-tight text-slate-900 sm:text-5xl lg:text-6xl">
|
||||
<span class="block">统一身份与权限</span>
|
||||
<span class="mt-2 block bg-gradient-to-r from-sky-600 to-indigo-600 bg-clip-text text-transparent">
|
||||
赋能云原生应用
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
{/* Description - High contrast with text width constraints */}
|
||||
<p class="mx-auto mb-8 max-w-[70ch] text-lg leading-relaxed text-slate-600 sm:text-xl">
|
||||
XControl 是一款现代化的云原生身份认证与访问控制平台,为您的应用提供企业级安全保障。
|
||||
集成多因素认证、细粒度权限管理与审计日志,助力您构建安全可靠的云原生生态。
|
||||
</p>
|
||||
|
||||
{/* Highlight Features List */}
|
||||
<div class="mx-auto mb-10 max-w-2xl">
|
||||
<ul class="grid gap-3 text-left sm:grid-cols-2">
|
||||
<li class="flex items-start gap-2 text-slate-700">
|
||||
<svg class="mt-0.5 h-5 w-5 flex-shrink-0 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<span class="text-base">企业级多因素认证 (MFA)</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-slate-700">
|
||||
<svg class="mt-0.5 h-5 w-5 flex-shrink-0 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<span class="text-base">细粒度权限控制 (RBAC/ABAC)</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-slate-700">
|
||||
<svg class="mt-0.5 h-5 w-5 flex-shrink-0 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<span class="text-base">完整审计日志与合规支持</span>
|
||||
</li>
|
||||
<li class="flex items-start gap-2 text-slate-700">
|
||||
<svg class="mt-0.5 h-5 w-5 flex-shrink-0 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<span class="text-base">云原生架构,开箱即用</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* CTA Buttons - Will be replaced by CtaButtons component */}
|
||||
<div class="flex flex-col items-center justify-center gap-4 sm:flex-row">
|
||||
<a
|
||||
href="/register"
|
||||
class="group relative inline-flex items-center justify-center gap-2 overflow-hidden rounded-xl bg-gradient-to-r from-sky-600 to-indigo-600 px-8 py-4 font-semibold text-white shadow-lg transition-all hover:scale-105 hover:shadow-xl active:scale-100"
|
||||
>
|
||||
<span class="absolute inset-0 bg-gradient-to-r from-sky-700 to-indigo-700 opacity-0 transition-opacity group-hover:opacity-100" />
|
||||
<span class="relative">开始使用</span>
|
||||
<svg class="relative h-5 w-5 transition-transform group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/docs"
|
||||
class="group inline-flex items-center justify-center gap-2 rounded-xl border-2 border-slate-300 bg-white px-8 py-4 font-semibold text-slate-900 shadow-sm transition-all hover:border-sky-400 hover:bg-slate-50 hover:shadow-md active:scale-95"
|
||||
>
|
||||
<span>查看文档</span>
|
||||
<svg class="h-5 w-5 transition-transform group-hover:translate-x-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Trust indicators */}
|
||||
<div class="mt-12 flex flex-wrap items-center justify-center gap-6 text-sm text-slate-500">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-5 w-5 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
<span>SOC 2 认证</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-5 w-5 text-sky-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
<span>端到端加密</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="h-5 w-5 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
<span>99.99% 可用性</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@ -4,12 +4,12 @@
|
||||
"tasks": {
|
||||
"dev": "deno run -A --watch=static/,routes/ dev.ts",
|
||||
"dev:full": "deno task css:watch & deno task dev",
|
||||
"start": "deno run -A main.ts --port 3000",
|
||||
"start": "deno run -A main.ts",
|
||||
"css:build": "deno run -A npm:tailwindcss@3.4.3 -i ./app/globals.css -o ./static/styles/globals.css --minify",
|
||||
"css:watch": "deno run -A npm:tailwindcss@3.4.3 -i ./app/globals.css -o ./static/styles/globals.css --watch",
|
||||
"prebuild": "deno run -A scripts/build-manifest.ts && deno run -A scripts/export-slugs.ts && deno run -A scripts/scan-md.ts && deno run -A scripts/fetch-dl-index.ts",
|
||||
"build": "deno run -A scripts/build.ts",
|
||||
"preview": "deno run -A main.ts --port 3000",
|
||||
"preview": "deno run -A main.ts",
|
||||
"update": "deno run -A -r https://fresh.deno.dev/update .",
|
||||
"lint": "deno lint",
|
||||
"fmt": "deno fmt",
|
||||
@ -27,7 +27,7 @@
|
||||
"$fresh/": "https://deno.land/x/fresh@1.7.3/",
|
||||
"preact": "https://esm.sh/preact@10.22.0",
|
||||
"preact/": "https://esm.sh/preact@10.22.0/",
|
||||
"preact/hooks": "https://esm.sh/preact@10.19.6/hooks",
|
||||
"preact/hooks": "https://esm.sh/preact@10.22.0/hooks",
|
||||
"preact-render-to-string": "https://esm.sh/*preact-render-to-string@6.3.1",
|
||||
"@preact/signals": "https://esm.sh/*@preact/signals@1.2.2",
|
||||
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1",
|
||||
|
||||
@ -28,6 +28,7 @@ import * as $MobileMenu from './islands/MobileMenu.tsx'
|
||||
import * as $Navbar from './islands/Navbar.tsx'
|
||||
import * as $RegisterForm from './islands/RegisterForm.tsx'
|
||||
import * as $SearchDialog from './islands/SearchDialog.tsx'
|
||||
import * as $home_ShowcaseCarousel from './islands/home/ShowcaseCarousel.tsx'
|
||||
import * as $panel_Header from './islands/panel/Header.tsx'
|
||||
import * as $panel_PanelLayout from './islands/panel/PanelLayout.tsx'
|
||||
import * as $panel_Sidebar from './islands/panel/Sidebar.tsx'
|
||||
@ -63,6 +64,7 @@ const manifest = {
|
||||
'./islands/Navbar.tsx': $Navbar,
|
||||
'./islands/RegisterForm.tsx': $RegisterForm,
|
||||
'./islands/SearchDialog.tsx': $SearchDialog,
|
||||
'./islands/home/ShowcaseCarousel.tsx': $home_ShowcaseCarousel,
|
||||
'./islands/panel/Header.tsx': $panel_Header,
|
||||
'./islands/panel/PanelLayout.tsx': $panel_PanelLayout,
|
||||
'./islands/panel/Sidebar.tsx': $panel_Sidebar,
|
||||
|
||||
@ -37,6 +37,18 @@ export default function Navbar({ language, user, pathname = '/' }: NavbarProps)
|
||||
|
||||
const isHiddenRoute = pathname ? ['/login', '/register'].some((prefix) => pathname.startsWith(prefix)) : false
|
||||
|
||||
// Generate language toggle URL that preserves current path and params
|
||||
const getLanguageToggleUrl = () => {
|
||||
if (typeof window === 'undefined') {
|
||||
// Server-side fallback
|
||||
return language === 'zh' ? '?lang=en' : '?lang=zh'
|
||||
}
|
||||
const newLang = language === 'zh' ? 'en' : 'zh'
|
||||
const url = new URL(window.location.href)
|
||||
url.searchParams.set('lang', newLang)
|
||||
return `${url.pathname}${url.search}`
|
||||
}
|
||||
|
||||
const isChinese = language === 'zh'
|
||||
const labels = {
|
||||
home: isChinese ? '首页' : 'Home',
|
||||
@ -267,7 +279,7 @@ export default function Navbar({ language, user, pathname = '/' }: NavbarProps)
|
||||
|
||||
{/* Language Toggle */}
|
||||
<a
|
||||
href={language === 'zh' ? '?lang=en' : '?lang=zh'}
|
||||
href={getLanguageToggleUrl()}
|
||||
class="rounded-full border border-brand-border px-3 py-1.5 text-sm text-brand-heading transition hover:border-brand hover:bg-brand-surface"
|
||||
>
|
||||
{language === 'zh' ? 'English' : '中文'}
|
||||
@ -414,7 +426,7 @@ export default function Navbar({ language, user, pathname = '/' }: NavbarProps)
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<a
|
||||
href={language === 'zh' ? '?lang=en' : '?lang=zh'}
|
||||
href={getLanguageToggleUrl()}
|
||||
class="rounded-lg border border-brand-border px-3 py-2 text-sm text-brand-heading transition hover:bg-brand-surface"
|
||||
>
|
||||
{language === 'zh' ? 'Switch to English' : '切换到中文'}
|
||||
|
||||
202
dashboard-fresh/islands/home/ShowcaseCarousel.tsx
Normal file
202
dashboard-fresh/islands/home/ShowcaseCarousel.tsx
Normal file
@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Showcase Carousel Island - Interactive Component
|
||||
*
|
||||
* Auto-playing carousel with smooth transitions to showcase platform features
|
||||
* Uses Preact signals for state management
|
||||
*/
|
||||
|
||||
import { useSignal, useComputed } from '@preact/signals'
|
||||
import { useEffect } from 'preact/hooks'
|
||||
|
||||
interface ShowcaseItem {
|
||||
title: string
|
||||
description: string
|
||||
image: string
|
||||
category: string
|
||||
}
|
||||
|
||||
const SHOWCASE_ITEMS: ShowcaseItem[] = [
|
||||
{
|
||||
category: '身份认证',
|
||||
title: '多因素认证 (MFA)',
|
||||
description: '支持 TOTP、SMS、邮件等多种认证方式,保障账户安全',
|
||||
image: '/images/showcase/mfa.svg',
|
||||
},
|
||||
{
|
||||
category: '权限管理',
|
||||
title: '细粒度访问控制',
|
||||
description: '基于角色和属性的权限控制,灵活配置访问策略',
|
||||
image: '/images/showcase/rbac.svg',
|
||||
},
|
||||
{
|
||||
category: '审计合规',
|
||||
title: '完整审计日志',
|
||||
description: '记录所有操作日志,满足合规性要求,支持审计追溯',
|
||||
image: '/images/showcase/audit.svg',
|
||||
},
|
||||
{
|
||||
category: '开发者工具',
|
||||
title: 'API 与 SDK',
|
||||
description: '提供 RESTful API 和多语言 SDK,快速集成到您的应用',
|
||||
image: '/images/showcase/api.svg',
|
||||
},
|
||||
]
|
||||
|
||||
interface ShowcaseCarouselProps {
|
||||
autoPlayInterval?: number
|
||||
className?: string
|
||||
}
|
||||
|
||||
export default function ShowcaseCarousel({
|
||||
autoPlayInterval = 5000,
|
||||
className = '',
|
||||
}: ShowcaseCarouselProps) {
|
||||
const currentIndex = useSignal(0)
|
||||
const isPaused = useSignal(false)
|
||||
|
||||
const currentItem = useComputed(() => SHOWCASE_ITEMS[currentIndex.value])
|
||||
|
||||
// Auto-play functionality
|
||||
useEffect(() => {
|
||||
if (isPaused.value) return
|
||||
|
||||
const interval = setInterval(() => {
|
||||
currentIndex.value = (currentIndex.value + 1) % SHOWCASE_ITEMS.length
|
||||
}, autoPlayInterval)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [isPaused.value, autoPlayInterval])
|
||||
|
||||
const goToSlide = (index: number) => {
|
||||
currentIndex.value = index
|
||||
}
|
||||
|
||||
const goToPrevious = () => {
|
||||
currentIndex.value = (currentIndex.value - 1 + SHOWCASE_ITEMS.length) % SHOWCASE_ITEMS.length
|
||||
}
|
||||
|
||||
const goToNext = () => {
|
||||
currentIndex.value = (currentIndex.value + 1) % SHOWCASE_ITEMS.length
|
||||
}
|
||||
|
||||
return (
|
||||
<section class={`bg-white py-20 md:py-32 ${className}`}>
|
||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
{/* Section Header */}
|
||||
<div class="mx-auto mb-16 max-w-2xl text-center">
|
||||
<p class="mb-3 text-sm font-semibold uppercase tracking-wide text-sky-600">
|
||||
核心功能
|
||||
</p>
|
||||
<h2 class="mb-4 text-3xl font-extrabold text-slate-900 sm:text-4xl">
|
||||
一站式云原生身份与权限解决方案
|
||||
</h2>
|
||||
<p class="text-lg text-slate-600">
|
||||
集成企业级安全功能,助力您快速构建安全可靠的云原生应用
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Carousel Container */}
|
||||
<div
|
||||
class="relative overflow-hidden rounded-3xl border border-slate-200 bg-gradient-to-br from-slate-50 to-white shadow-xl"
|
||||
onMouseEnter={() => (isPaused.value = true)}
|
||||
onMouseLeave={() => (isPaused.value = false)}
|
||||
>
|
||||
{/* Main Content */}
|
||||
<div class="grid gap-8 p-8 md:grid-cols-2 md:gap-12 md:p-12 lg:p-16">
|
||||
{/* Left: Text Content */}
|
||||
<div class="flex flex-col justify-center">
|
||||
<span class="mb-4 inline-block w-fit rounded-full bg-sky-100 px-4 py-1.5 text-sm font-semibold text-sky-700">
|
||||
{currentItem.value.category}
|
||||
</span>
|
||||
|
||||
<h3 class="mb-4 text-3xl font-bold text-slate-900 lg:text-4xl">
|
||||
{currentItem.value.title}
|
||||
</h3>
|
||||
|
||||
<p class="mb-8 text-lg leading-relaxed text-slate-600">
|
||||
{currentItem.value.description}
|
||||
</p>
|
||||
|
||||
{/* Navigation Dots */}
|
||||
<div class="flex items-center gap-3">
|
||||
{SHOWCASE_ITEMS.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => goToSlide(index)}
|
||||
class={`h-2 rounded-full transition-all ${
|
||||
index === currentIndex.value
|
||||
? 'w-12 bg-sky-600'
|
||||
: 'w-2 bg-slate-300 hover:bg-slate-400'
|
||||
}`}
|
||||
aria-label={`Go to slide ${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: Image/Visual Content */}
|
||||
<div class="relative flex items-center justify-center">
|
||||
<div class="relative h-80 w-full overflow-hidden rounded-2xl bg-gradient-to-br from-sky-100 to-indigo-100 shadow-lg">
|
||||
{/* Placeholder for image - Replace with actual images */}
|
||||
<div class="flex h-full items-center justify-center">
|
||||
<div class="text-center">
|
||||
<div class="mb-4 inline-flex h-24 w-24 items-center justify-center rounded-2xl bg-white/80 shadow-lg backdrop-blur-sm">
|
||||
<svg class="h-12 w-12 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-sm font-medium text-slate-600">
|
||||
{currentItem.value.category}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Image overlay effect */}
|
||||
<div class="pointer-events-none absolute inset-0 bg-gradient-to-t from-white/10 to-transparent" />
|
||||
</div>
|
||||
|
||||
{/* Decorative elements */}
|
||||
<div class="pointer-events-none absolute -right-8 -top-8 h-32 w-32 rounded-full bg-sky-200/30 blur-2xl" />
|
||||
<div class="pointer-events-none absolute -bottom-8 -left-8 h-32 w-32 rounded-full bg-indigo-200/30 blur-2xl" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navigation Arrows */}
|
||||
<button
|
||||
onClick={goToPrevious}
|
||||
class="absolute left-4 top-1/2 -translate-y-1/2 rounded-full bg-white/90 p-3 shadow-lg backdrop-blur-sm transition-all hover:bg-white hover:scale-110 active:scale-95"
|
||||
aria-label="Previous slide"
|
||||
>
|
||||
<svg class="h-6 w-6 text-slate-900" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={goToNext}
|
||||
class="absolute right-4 top-1/2 -translate-y-1/2 rounded-full bg-white/90 p-3 shadow-lg backdrop-blur-sm transition-all hover:bg-white hover:scale-110 active:scale-95"
|
||||
aria-label="Next slide"
|
||||
>
|
||||
<svg class="h-6 w-6 text-slate-900" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Auto-play indicator */}
|
||||
{!isPaused.value && (
|
||||
<div class="mt-6 text-center">
|
||||
<p class="text-sm text-slate-500">
|
||||
自动轮播中 · 悬停暂停
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@ -14,6 +14,7 @@ import { DOMParser } from 'https://deno.land/x/deno_dom@v0.1.45/deno-dom-wasm.ts
|
||||
// Import Islands for client-side interactivity
|
||||
import Navbar from '@/islands/Navbar.tsx'
|
||||
import AskAIButton from '@/islands/AskAIButton.tsx'
|
||||
// import ShowcaseCarousel from '@/islands/home/ShowcaseCarousel.tsx'
|
||||
|
||||
// Supported languages
|
||||
type Language = 'zh' | 'en'
|
||||
@ -250,28 +251,40 @@ export default function HomePage({ data }: PageProps<HomePageData>) {
|
||||
<Navbar language={language} user={user} pathname="/" />
|
||||
|
||||
{/* Main Content with offset for fixed navbar */}
|
||||
<main class="relative flex flex-col bg-brand-surface text-brand-heading pt-24">
|
||||
{/* Hero Section - Operations */}
|
||||
<header class="relative isolate overflow-hidden py-20 sm:py-24">
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-brand via-brand/70 to-transparent" aria-hidden />
|
||||
<div class="absolute -right-32 top-10 h-64 w-64 rounded-full bg-white/20 blur-3xl" aria-hidden />
|
||||
<main
|
||||
class="relative flex flex-col bg-gradient-to-br from-sky-50 via-indigo-50/30 to-white text-slate-900"
|
||||
style="padding-top: var(--app-shell-nav-offset, 4rem)"
|
||||
>
|
||||
{/* Hero Section - Improved Design */}
|
||||
<header class="relative isolate overflow-hidden pt-16 pb-20 md:pt-24 md:pb-32">
|
||||
{/* Decorative blur elements for 2C visual appeal */}
|
||||
<div class="pointer-events-none absolute inset-0 overflow-hidden">
|
||||
<div class="absolute -top-40 -right-40 h-80 w-80 rounded-full bg-sky-200/30 blur-3xl" />
|
||||
<div class="absolute top-1/2 -left-40 h-96 w-96 rounded-full bg-indigo-200/20 blur-3xl" />
|
||||
<div class="absolute bottom-0 right-1/4 h-64 w-64 rounded-full bg-purple-200/20 blur-3xl" />
|
||||
</div>
|
||||
|
||||
<div class="relative px-4 sm:px-6 lg:px-8">
|
||||
<div class="mx-auto w-full max-w-6xl">
|
||||
<div class="rounded-3xl border border-white/30 bg-white/10 p-8 shadow-[0_30px_80px_rgba(0,0,0,0.18)] backdrop-blur-lg sm:p-10 lg:p-12">
|
||||
<div class="rounded-3xl border border-sky-200/50 bg-white/80 p-8 shadow-[0_30px_80px_rgba(0,0,0,0.08)] backdrop-blur-lg sm:p-10 lg:p-12">
|
||||
{shouldFallbackHero ? (
|
||||
<div
|
||||
class="prose prose-invert max-w-none text-white/90"
|
||||
class="prose prose-slate max-w-none"
|
||||
dangerouslySetInnerHTML={{ __html: sections.operations.html }}
|
||||
/>
|
||||
) : (
|
||||
<div class="grid gap-10 lg:grid-cols-[minmax(0,1.35fr)_minmax(0,1fr)] lg:gap-12">
|
||||
<div class="space-y-6 text-white">
|
||||
<div class="space-y-6">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<span class="inline-flex items-center rounded-full border border-white/30 bg-white/10 px-4 py-1 text-xs font-semibold uppercase tracking-[0.4em]">
|
||||
<span class="inline-flex items-center gap-2 rounded-full border border-sky-200 bg-sky-50 px-4 py-1.5 text-xs font-semibold uppercase tracking-[0.35em] text-sky-700 shadow-sm">
|
||||
<span class="flex h-2 w-2">
|
||||
<span class="absolute inline-flex h-2 w-2 animate-ping rounded-full bg-sky-400 opacity-75" />
|
||||
<span class="relative inline-flex h-2 w-2 rounded-full bg-sky-500" />
|
||||
</span>
|
||||
CloudNative Suite
|
||||
</span>
|
||||
{operationsUpdated && (
|
||||
<span class="text-xs text-white/80">
|
||||
<span class="text-xs text-slate-600">
|
||||
{language === 'zh'
|
||||
? `最近更新 · ${operationsUpdated}`
|
||||
: `Last updated · ${operationsUpdated}`}
|
||||
@ -279,11 +292,11 @@ export default function HomePage({ data }: PageProps<HomePageData>) {
|
||||
)}
|
||||
</div>
|
||||
{heroContent.heading && (
|
||||
<h1 class="text-[32px] font-bold leading-tight sm:text-[36px]">
|
||||
<h1 class="text-4xl font-extrabold leading-tight tracking-tight text-slate-900 sm:text-5xl lg:text-6xl">
|
||||
{heroContent.heading}
|
||||
</h1>
|
||||
)}
|
||||
<div class="space-y-4 text-base text-white/85">
|
||||
<div class="max-w-[70ch] space-y-4 text-lg text-slate-600">
|
||||
{heroContent.paragraphs.map((paragraph, index) => (
|
||||
<p
|
||||
key={index}
|
||||
@ -292,30 +305,39 @@ export default function HomePage({ data }: PageProps<HomePageData>) {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-3 pt-2 text-sm font-semibold">
|
||||
<div class="flex flex-wrap gap-4 pt-2">
|
||||
<a
|
||||
href="/demo"
|
||||
class="inline-flex items-center rounded-full bg-white px-5 py-2 text-brand shadow-[0_4px_20px_rgba(51,102,255,0.25)] transition hover:bg-brand-light hover:text-white"
|
||||
class="group relative inline-flex items-center justify-center gap-2 overflow-hidden rounded-xl bg-gradient-to-r from-sky-600 to-indigo-600 px-8 py-4 font-semibold text-white shadow-lg transition-all hover:scale-105 hover:shadow-xl active:scale-100"
|
||||
>
|
||||
{primaryCtaLabel}
|
||||
<span class="absolute inset-0 bg-gradient-to-r from-sky-700 to-indigo-700 opacity-0 transition-opacity group-hover:opacity-100" />
|
||||
<span class="relative">{primaryCtaLabel}</span>
|
||||
<svg class="relative h-5 w-5 transition-transform group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="/download"
|
||||
class="inline-flex items-center rounded-full border border-white/40 px-5 py-2 text-white/85 transition hover:border-white hover:text-white"
|
||||
class="group inline-flex items-center justify-center gap-2 rounded-xl border-2 border-slate-300 bg-white px-8 py-4 font-semibold text-slate-900 shadow-sm transition-all hover:border-sky-400 hover:bg-slate-50 hover:shadow-md active:scale-95"
|
||||
>
|
||||
{secondaryCtaLabel}
|
||||
<span>{secondaryCtaLabel}</span>
|
||||
<svg class="h-5 w-5 transition-transform group-hover:translate-x-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{heroContent.highlights.length > 0 && (
|
||||
<div class="space-y-4 rounded-2xl border border-white/20 bg-white/10 p-6 text-white/90 shadow-[0_10px_30px_rgba(0,0,0,0.18)]">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.35em] text-white/90">
|
||||
<div class="space-y-4 rounded-2xl border border-sky-200/50 bg-sky-50/50 p-6 shadow-lg backdrop-blur-sm">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.35em] text-sky-700">
|
||||
{language === 'zh' ? '核心能力' : 'Key Capabilities'}
|
||||
</p>
|
||||
<ul class="space-y-3 text-sm">
|
||||
{heroContent.highlights.map((item, index) => (
|
||||
<li key={index} class="flex items-start gap-3">
|
||||
<span class="mt-1 h-2 w-2 flex-shrink-0 rounded-full bg-white" aria-hidden />
|
||||
<li key={index} class="flex items-start gap-3 text-slate-700">
|
||||
<svg class="mt-0.5 h-5 w-5 flex-shrink-0 text-sky-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<span
|
||||
class="leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: item.html }}
|
||||
@ -332,6 +354,9 @@ export default function HomePage({ data }: PageProps<HomePageData>) {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Showcase Carousel - New Interactive Section */}
|
||||
{/* <ShowcaseCarousel /> */}
|
||||
|
||||
{/* Main Content Sections */}
|
||||
<section class="relative isolate py-20 sm:py-24">
|
||||
<div class="absolute inset-x-0 top-0 h-px bg-brand-border/70" aria-hidden />
|
||||
|
||||
@ -33,7 +33,7 @@ export default function NavbarDemoPage(props: PageProps) {
|
||||
<Navbar language={language} user={user} pathname={props.url.pathname} />
|
||||
|
||||
{/* Main content with offset for fixed navbar */}
|
||||
<main class="min-h-screen bg-brand-surface pt-24">
|
||||
<main class="min-h-screen bg-brand-surface" style="padding-top: var(--app-shell-nav-offset, 6rem)">
|
||||
<div class="mx-auto max-w-7xl px-6 py-12 sm:px-8">
|
||||
<div class="space-y-8">
|
||||
{/* Header */}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user