's'
This commit is contained in:
@@ -15,44 +15,44 @@ const linkIcons: Record<(typeof linkKeys)[number], string> = {
|
||||
export default function Community() {
|
||||
const { t } = useTranslation("community");
|
||||
return (
|
||||
<section id="community" className="py-20 sm:py-28">
|
||||
<section id="community" className="py-20 sm:py-28 dark:bg-slate-950">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
{/* 数字游民社区 - 优化布局 */}
|
||||
<div className="mb-20">
|
||||
<Link
|
||||
href="/join"
|
||||
className="group block overflow-hidden rounded-2xl border border-sky-200/80 bg-gradient-to-br from-sky-50 via-white to-cyan-50/60 p-8 shadow-sm transition-all duration-300 hover:border-sky-300 hover:shadow-xl hover:shadow-sky-100/50 sm:p-10"
|
||||
className="group block overflow-hidden rounded-2xl border border-sky-200/80 bg-gradient-to-br from-sky-50 via-white to-cyan-50/60 p-8 shadow-sm transition-all duration-300 hover:border-sky-300 hover:shadow-xl hover:shadow-sky-100/50 dark:border-sky-800/80 dark:from-sky-900/30 dark:via-slate-900 dark:to-cyan-900/20 dark:hover:border-sky-700 dark:hover:shadow-sky-900/30 sm:p-10"
|
||||
>
|
||||
<div className="flex flex-col gap-6 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="mb-3 inline-flex items-center gap-2 rounded-full bg-sky-100 px-4 py-1.5 text-sm font-semibold text-sky-700">
|
||||
<div className="mb-3 inline-flex items-center gap-2 rounded-full bg-sky-100 px-4 py-1.5 text-sm font-semibold text-sky-700 dark:bg-sky-900/50 dark:text-sky-300">
|
||||
<span className="text-lg">👥</span>
|
||||
{t("nomadCommunity")}
|
||||
</div>
|
||||
<h3 className="mb-3 text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl">
|
||||
<h3 className="mb-3 text-2xl font-bold tracking-tight text-slate-900 dark:text-slate-100 sm:text-3xl">
|
||||
{t("joinTitle")}
|
||||
</h3>
|
||||
<p className="mb-4 max-w-xl text-base leading-relaxed text-slate-600">
|
||||
<p className="mb-4 max-w-xl text-base leading-relaxed text-slate-600 dark:text-slate-400">
|
||||
{t("joinDesc")}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<span className="rounded-lg bg-white/90 px-3 py-1.5 text-sm font-medium text-slate-600 shadow-sm">
|
||||
<span className="rounded-lg bg-white/90 px-3 py-1.5 text-sm font-medium text-slate-600 shadow-sm dark:bg-slate-800/90 dark:text-slate-300">
|
||||
{t("applyForm")}
|
||||
</span>
|
||||
<span className="rounded-lg bg-white/90 px-3 py-1.5 text-sm font-medium text-slate-600 shadow-sm">
|
||||
<span className="rounded-lg bg-white/90 px-3 py-1.5 text-sm font-medium text-slate-600 shadow-sm dark:bg-slate-800/90 dark:text-slate-300">
|
||||
{t("approveJoin")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-4 rounded-2xl bg-white/90 p-6 shadow-md backdrop-blur-sm transition-transform group-hover:scale-[1.02] sm:p-8">
|
||||
<div className="flex shrink-0 items-center gap-4 rounded-2xl bg-white/90 p-6 shadow-md backdrop-blur-sm transition-transform dark:bg-slate-800/90 group-hover:scale-[1.02] sm:p-8">
|
||||
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-sky-400 to-cyan-500 text-3xl shadow-lg shadow-sky-200/50">
|
||||
📝
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<div className="font-bold text-slate-800">
|
||||
<div className="font-bold text-slate-800 dark:text-slate-200">
|
||||
{t("applyNow")}
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-slate-500">
|
||||
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
||||
{t("applyHint")}
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,13 +62,13 @@ export default function Community() {
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="mb-2 text-sm font-semibold text-slate-400">
|
||||
<div className="mb-2 text-sm font-semibold text-slate-400 dark:text-slate-500">
|
||||
{t("openSource")}
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
|
||||
<h2 className="text-3xl font-bold tracking-tight text-slate-900 dark:text-slate-100 sm:text-4xl">
|
||||
{t("contributeTitle")} <span className="gradient-text">{t("contributeHighlight")}</span>
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600">
|
||||
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600 dark:text-slate-400">
|
||||
{t("contributeSubtitle")}
|
||||
<br />
|
||||
{t("contributeSubtitle2")}
|
||||
@@ -79,14 +79,14 @@ export default function Community() {
|
||||
{linkKeys.map((key) => (
|
||||
<div
|
||||
key={key}
|
||||
className="card-hover flex items-start gap-4 rounded-xl border border-slate-100 bg-white p-5 shadow-sm"
|
||||
className="card-hover flex items-start gap-4 rounded-xl border border-slate-100 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900"
|
||||
>
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-slate-50 text-xl">
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-slate-50 text-xl dark:bg-slate-800">
|
||||
{linkIcons[key]}
|
||||
</span>
|
||||
<div>
|
||||
<h4 className="font-bold text-slate-900">{t(`links.${key}.title`)}</h4>
|
||||
<p className="mt-1 text-sm text-slate-500">{t(`links.${key}.desc`)}</p>
|
||||
<h4 className="font-bold text-slate-900 dark:text-slate-100">{t(`links.${key}.title`)}</h4>
|
||||
<p className="mt-1 text-sm text-slate-500 dark:text-slate-400">{t(`links.${key}.desc`)}</p>
|
||||
<span className="mt-2 inline-block text-sm font-medium text-sky-600">
|
||||
{t(`links.${key}.label`)}
|
||||
</span>
|
||||
@@ -96,15 +96,15 @@ export default function Community() {
|
||||
</div>
|
||||
|
||||
<div className="mt-12 text-center">
|
||||
<div className="mb-4 text-lg font-bold text-slate-900">
|
||||
<div className="mb-4 text-lg font-bold text-slate-900 dark:text-slate-100">
|
||||
{t("betterTitle")}
|
||||
</div>
|
||||
<p className="mb-6 text-slate-600">
|
||||
<p className="mb-6 text-slate-600 dark:text-slate-400">
|
||||
{t("betterDesc")}
|
||||
<br />
|
||||
{t("betterDesc2")}
|
||||
</p>
|
||||
<button className="inline-flex items-center gap-2 rounded-full bg-slate-900 px-8 py-3 font-semibold text-white transition-colors hover:bg-slate-800">
|
||||
<button className="inline-flex items-center gap-2 rounded-full bg-slate-900 px-8 py-3 font-semibold text-white transition-colors hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-slate-200">
|
||||
{t("starFork")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -11,13 +11,13 @@ export default function Features() {
|
||||
key,
|
||||
}));
|
||||
return (
|
||||
<section id="features" className="py-20 sm:py-28">
|
||||
<section id="features" className="py-20 sm:py-28 dark:bg-slate-950">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
|
||||
<h2 className="text-3xl font-bold tracking-tight text-slate-900 dark:text-slate-100 sm:text-4xl">
|
||||
{t("title")} <span className="gradient-text">{t("titleHighlight")}</span>{t("titleSuffix")}
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600">
|
||||
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600 dark:text-slate-400">
|
||||
{t("subtitle")}
|
||||
<br className="hidden sm:block" />
|
||||
{t("subtitle2")}
|
||||
@@ -28,21 +28,21 @@ export default function Features() {
|
||||
{features.map((f) => (
|
||||
<div
|
||||
key={f.key}
|
||||
className="card-hover rounded-2xl border border-slate-100 bg-white p-8 shadow-sm"
|
||||
className="card-hover rounded-2xl border border-slate-100 bg-white p-8 shadow-sm dark:border-slate-800 dark:bg-slate-900"
|
||||
>
|
||||
<div className="mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-sky-50 text-3xl">
|
||||
<div className="mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-sky-50 text-3xl dark:bg-sky-900/50">
|
||||
{f.icon}
|
||||
</div>
|
||||
<h3 className="mb-3 text-xl font-bold text-slate-900">
|
||||
<h3 className="mb-3 text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||
{t(`items.${f.key}.title`)}
|
||||
</h3>
|
||||
<p className="leading-relaxed text-slate-600">{t(`items.${f.key}.desc`)}</p>
|
||||
<p className="leading-relaxed text-slate-600 dark:text-slate-400">{t(`items.${f.key}.desc`)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-12 text-center">
|
||||
<div className="inline-flex items-center gap-2 rounded-full border border-amber-200 bg-amber-50 px-5 py-2 text-sm font-medium text-amber-700">
|
||||
<div className="inline-flex items-center gap-2 rounded-full border border-amber-200 bg-amber-50 px-5 py-2 text-sm font-medium text-amber-700 dark:border-amber-800 dark:bg-amber-900/50 dark:text-amber-300">
|
||||
<span>🔥</span>
|
||||
{t("stat")} <span className="font-bold">{t("statCount")}</span> {t("statSuffix")}
|
||||
</div>
|
||||
|
||||
@@ -43,15 +43,15 @@ const footerSections = [
|
||||
export default function Footer() {
|
||||
const { t } = useTranslation("footer");
|
||||
return (
|
||||
<footer className="border-t border-slate-100 bg-white">
|
||||
<footer className="border-t border-slate-100 bg-white dark:border-slate-800 dark:bg-slate-900">
|
||||
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8 lg:py-16">
|
||||
<div className="grid gap-8 sm:grid-cols-2 lg:grid-cols-5">
|
||||
<div className="lg:col-span-1">
|
||||
<Link href="/" className="flex items-center gap-2 text-lg font-bold">
|
||||
<Link href="/" className="flex items-center gap-2 text-lg font-bold text-slate-900 dark:text-slate-100">
|
||||
<span className="text-2xl">🌍</span>
|
||||
<span>{t("siteName")}</span>
|
||||
</Link>
|
||||
<p className="mt-3 text-sm leading-relaxed text-slate-500">
|
||||
<p className="mt-3 text-sm leading-relaxed text-slate-500 dark:text-slate-400">
|
||||
{t("tagline")}
|
||||
<br />
|
||||
{t("tagline2")}
|
||||
@@ -60,7 +60,7 @@ export default function Footer() {
|
||||
|
||||
{footerSections.map((section) => (
|
||||
<div key={section.titleKey}>
|
||||
<h4 className="mb-4 text-sm font-bold text-slate-900">
|
||||
<h4 className="mb-4 text-sm font-bold text-slate-900 dark:text-slate-100">
|
||||
{t(`sections.${section.titleKey}`)}
|
||||
</h4>
|
||||
<ul className="space-y-2.5">
|
||||
@@ -68,7 +68,7 @@ export default function Footer() {
|
||||
<li key={link.labelKey}>
|
||||
<a
|
||||
href={link.href}
|
||||
className="text-sm text-slate-500 transition-colors hover:text-slate-700"
|
||||
className="text-sm text-slate-500 transition-colors hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
|
||||
>
|
||||
{t(`links.${link.labelKey}`)}
|
||||
</a>
|
||||
@@ -79,11 +79,11 @@ export default function Footer() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-12 flex flex-col items-center justify-between gap-4 border-t border-slate-100 pt-8 sm:flex-row">
|
||||
<p className="text-sm text-slate-400">
|
||||
<div className="mt-12 flex flex-col items-center justify-between gap-4 border-t border-slate-100 pt-8 dark:border-slate-800 sm:flex-row">
|
||||
<p className="text-sm text-slate-400 dark:text-slate-500">
|
||||
{t("madeBy")}
|
||||
</p>
|
||||
<p className="text-sm text-slate-400">{t("openSource")}</p>
|
||||
<p className="text-sm text-slate-400 dark:text-slate-500">{t("openSource")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Link, useLocale, usePathname, useRouter, useTranslation } from "@/i18n/navigation";
|
||||
import AuthModal from "./AuthModal";
|
||||
import ThemeToggle from "./ThemeToggle";
|
||||
|
||||
const navLinks = [
|
||||
{ labelKey: "roadmap" as const, href: "#roadmap" },
|
||||
@@ -47,13 +48,13 @@ export default function Header() {
|
||||
<header
|
||||
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
|
||||
scrolled
|
||||
? "bg-white/80 backdrop-blur-lg shadow-sm"
|
||||
? "bg-white/80 backdrop-blur-lg shadow-sm dark:bg-slate-900/80 dark:shadow-slate-950/50"
|
||||
: "bg-transparent"
|
||||
}`}
|
||||
>
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex h-16 items-center justify-between">
|
||||
<Link href="/" className="flex items-center gap-2 text-lg font-bold">
|
||||
<Link href="/" className="flex items-center gap-2 text-lg font-bold text-slate-900 dark:text-slate-100">
|
||||
<span className="text-2xl">🌍</span>
|
||||
<span className="hidden sm:inline">{t("siteName")}</span>
|
||||
<span className="sm:hidden">{t("siteNameShort")}</span>
|
||||
@@ -64,7 +65,7 @@ export default function Header() {
|
||||
<a
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
className="rounded-lg px-3 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900"
|
||||
className="rounded-lg px-3 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
|
||||
>
|
||||
{tNav(link.labelKey)}
|
||||
</a>
|
||||
@@ -72,7 +73,7 @@ export default function Header() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.replace(pathname, { locale: locale === "zh" ? "en" : "zh" })}
|
||||
className="rounded-lg px-3 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900"
|
||||
className="rounded-lg px-3 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
|
||||
>
|
||||
{locale === "zh" ? "EN" : "中文"}
|
||||
</button>
|
||||
@@ -81,12 +82,12 @@ export default function Header() {
|
||||
<div className="flex items-center gap-3">
|
||||
{userEmail ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="hidden max-w-[120px] truncate text-sm text-slate-600 sm:inline">
|
||||
<span className="hidden max-w-[120px] truncate text-sm text-slate-600 dark:text-slate-400 sm:inline">
|
||||
{userEmail}
|
||||
</span>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="rounded-full border border-slate-200 px-4 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-50"
|
||||
className="rounded-full border border-slate-200 px-4 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-50 dark:border-slate-700 dark:text-slate-400 dark:hover:bg-slate-800"
|
||||
>
|
||||
{t("logout")}
|
||||
</button>
|
||||
@@ -94,21 +95,22 @@ export default function Header() {
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setAuthOpen(true)}
|
||||
className="hidden rounded-full border border-slate-200 px-4 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-50 sm:inline-flex"
|
||||
className="hidden rounded-full border border-slate-200 px-4 py-2 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-50 dark:border-slate-700 dark:text-slate-400 dark:hover:bg-slate-800 sm:inline-flex"
|
||||
>
|
||||
{t("loginRegister")}
|
||||
</button>
|
||||
)}
|
||||
<ThemeToggle />
|
||||
<a
|
||||
href="#roadmap"
|
||||
className="hidden rounded-full bg-sky-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-sky-600 sm:inline-flex"
|
||||
className="hidden rounded-full bg-sky-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-sky-600 dark:bg-sky-600 dark:hover:bg-sky-500 sm:inline-flex"
|
||||
>
|
||||
{t("explore")}
|
||||
</a>
|
||||
|
||||
<button
|
||||
onClick={() => setMobileOpen(!mobileOpen)}
|
||||
className="inline-flex items-center justify-center rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100 md:hidden"
|
||||
className="inline-flex items-center justify-center rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100 dark:text-slate-400 dark:hover:bg-slate-800 md:hidden"
|
||||
aria-label={t("toggleMenu")}
|
||||
>
|
||||
{mobileOpen ? (
|
||||
@@ -126,14 +128,14 @@ export default function Header() {
|
||||
</div>
|
||||
|
||||
{mobileOpen && (
|
||||
<div className="border-t border-slate-100 bg-white/95 backdrop-blur-lg md:hidden">
|
||||
<div className="border-t border-slate-100 bg-white/95 backdrop-blur-lg dark:border-slate-800 dark:bg-slate-900/95 md:hidden">
|
||||
<nav className="mx-auto max-w-7xl space-y-1 px-4 py-3">
|
||||
{navLinks.map((link) => (
|
||||
<a
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className="block rounded-lg px-3 py-2.5 text-base font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900"
|
||||
className="block rounded-lg px-3 py-2.5 text-base font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
|
||||
>
|
||||
{tNav(link.labelKey)}
|
||||
</a>
|
||||
@@ -144,12 +146,12 @@ export default function Header() {
|
||||
setMobileOpen(false);
|
||||
router.replace(pathname, { locale: locale === "zh" ? "en" : "zh" });
|
||||
}}
|
||||
className="block w-full rounded-lg px-3 py-2.5 text-left text-base font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900"
|
||||
className="block w-full rounded-lg px-3 py-2.5 text-left text-base font-medium text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
|
||||
>
|
||||
{locale === "zh" ? "EN" : "中文"}
|
||||
</button>
|
||||
{userEmail ? (
|
||||
<div className="mt-2 flex items-center justify-between rounded-lg border border-slate-100 bg-slate-50 px-4 py-2.5">
|
||||
<div className="mt-2 flex items-center justify-between rounded-lg border border-slate-100 bg-slate-50 px-4 py-2.5 dark:border-slate-700 dark:bg-slate-800">
|
||||
<span className="truncate text-sm text-slate-600">{userEmail}</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -167,7 +169,7 @@ export default function Header() {
|
||||
setMobileOpen(false);
|
||||
setAuthOpen(true);
|
||||
}}
|
||||
className="mt-2 block w-full rounded-full border border-slate-200 px-4 py-2.5 text-center text-base font-medium text-slate-600"
|
||||
className="mt-2 block w-full rounded-full border border-slate-200 px-4 py-2.5 text-center text-base font-medium text-slate-600 dark:border-slate-700 dark:text-slate-400"
|
||||
>
|
||||
{t("loginRegister")}
|
||||
</button>
|
||||
@@ -175,7 +177,7 @@ export default function Header() {
|
||||
<a
|
||||
href="#roadmap"
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className="mt-2 block rounded-full bg-sky-500 px-4 py-2.5 text-center text-base font-medium text-white transition-colors hover:bg-sky-600"
|
||||
className="mt-2 block rounded-full bg-sky-500 px-4 py-2.5 text-center text-base font-medium text-white transition-colors hover:bg-sky-600 dark:bg-sky-600 dark:hover:bg-sky-500"
|
||||
>
|
||||
{t("explore")}
|
||||
</a>
|
||||
|
||||
@@ -13,26 +13,26 @@ export default function Hero() {
|
||||
];
|
||||
return (
|
||||
<section className="relative overflow-hidden pt-24 pb-16 sm:pt-32 sm:pb-20">
|
||||
<div className="pointer-events-none absolute inset-0 bg-gradient-to-b from-sky-50/80 via-white to-white" />
|
||||
<div className="pointer-events-none absolute top-0 left-1/2 h-[600px] w-[800px] -translate-x-1/2 rounded-full bg-gradient-to-br from-sky-100/60 via-cyan-50/40 to-amber-50/30 blur-3xl" />
|
||||
<div className="pointer-events-none absolute inset-0 bg-gradient-to-b from-sky-50/80 via-white to-white dark:from-slate-900 dark:via-slate-900 dark:to-slate-950" />
|
||||
<div className="pointer-events-none absolute top-0 left-1/2 h-[600px] w-[800px] -translate-x-1/2 rounded-full bg-gradient-to-br from-sky-100/60 via-cyan-50/40 to-amber-50/30 blur-3xl dark:from-sky-900/20 dark:via-slate-800/30 dark:to-amber-900/10" />
|
||||
|
||||
<div className="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<div className="mb-6 inline-flex items-center gap-2 rounded-full border border-sky-200 bg-sky-50 px-4 py-1.5 text-sm font-medium text-sky-700">
|
||||
<div className="mb-6 inline-flex items-center gap-2 rounded-full border border-sky-200 bg-sky-50 px-4 py-1.5 text-sm font-medium text-sky-700 dark:border-sky-800 dark:bg-sky-900/50 dark:text-sky-300">
|
||||
<span>🎒</span>
|
||||
<span>{t("badge")}</span>
|
||||
</div>
|
||||
|
||||
<h1 className="mx-auto max-w-4xl text-4xl font-extrabold tracking-tight sm:text-5xl lg:text-7xl">
|
||||
<h1 className="mx-auto max-w-4xl text-4xl font-extrabold tracking-tight text-slate-900 dark:text-slate-100 sm:text-5xl lg:text-7xl">
|
||||
<span className="gradient-text">{t("title")}</span>
|
||||
<br className="sm:hidden" />
|
||||
{t("titleSuffix")}
|
||||
</h1>
|
||||
|
||||
<p className="mx-auto mt-6 max-w-2xl text-lg leading-relaxed text-slate-600 sm:text-xl">
|
||||
<p className="mx-auto mt-6 max-w-2xl text-lg leading-relaxed text-slate-600 dark:text-slate-400 sm:text-xl">
|
||||
{t("subtitle")}
|
||||
<br className="hidden sm:block" />
|
||||
<span className="text-slate-400 sm:text-slate-600">
|
||||
<span className="text-slate-400 dark:text-slate-500 sm:text-slate-600">
|
||||
{t("subtitleEn")}
|
||||
</span>
|
||||
</p>
|
||||
@@ -47,7 +47,7 @@ export default function Hero() {
|
||||
</a>
|
||||
<a
|
||||
href="#community"
|
||||
className="inline-flex w-full items-center justify-center gap-2 rounded-full border border-slate-200 bg-white px-8 py-3.5 text-base font-semibold text-slate-700 shadow-sm transition-all hover:border-slate-300 hover:bg-slate-50 sm:w-auto"
|
||||
className="inline-flex w-full items-center justify-center gap-2 rounded-full border border-slate-200 bg-white px-8 py-3.5 text-base font-semibold text-slate-700 shadow-sm transition-all hover:border-slate-300 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200 dark:hover:border-slate-600 dark:hover:bg-slate-700 sm:w-auto"
|
||||
>
|
||||
{t("ctaResources")}
|
||||
</a>
|
||||
@@ -60,12 +60,12 @@ export default function Hero() {
|
||||
{[...stats, ...stats].map((s, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex shrink-0 items-center gap-3 rounded-2xl border border-slate-100 bg-white/80 px-6 py-4 shadow-sm backdrop-blur-sm"
|
||||
className="flex shrink-0 items-center gap-3 rounded-2xl border border-slate-100 bg-white/80 px-6 py-4 shadow-sm backdrop-blur-sm dark:border-slate-700 dark:bg-slate-800/80"
|
||||
>
|
||||
<span className="text-2xl font-bold text-sky-600 sm:text-3xl">
|
||||
{s.value}
|
||||
</span>
|
||||
<span className="text-sm font-medium text-slate-500">
|
||||
<span className="text-sm font-medium text-slate-500 dark:text-slate-400">
|
||||
{t(`stats.${s.labelKey}`)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -78,12 +78,12 @@ export default function Hero() {
|
||||
{[...stats, ...stats].map((s, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex shrink-0 items-center gap-3 rounded-2xl border border-slate-100 bg-white/80 px-6 py-4 shadow-sm backdrop-blur-sm"
|
||||
className="flex shrink-0 items-center gap-3 rounded-2xl border border-slate-100 bg-white/80 px-6 py-4 shadow-sm backdrop-blur-sm dark:border-slate-700 dark:bg-slate-800/80"
|
||||
>
|
||||
<span className="text-2xl font-bold text-amber-500 sm:text-3xl">
|
||||
{s.value}
|
||||
</span>
|
||||
<span className="text-sm font-medium text-slate-500">
|
||||
<span className="text-sm font-medium text-slate-500 dark:text-slate-400">
|
||||
{t(`stats.${s.labelKey}`)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -15,13 +15,13 @@ const days = [
|
||||
export default function Roadmap() {
|
||||
const { t } = useTranslation("roadmap");
|
||||
return (
|
||||
<section id="roadmap" className="bg-slate-50 py-20 sm:py-28">
|
||||
<section id="roadmap" className="bg-slate-50 py-20 sm:py-28 dark:bg-slate-900">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
|
||||
<h2 className="text-3xl font-bold tracking-tight text-slate-900 dark:text-slate-100 sm:text-4xl">
|
||||
{t("title")} <span className="gradient-text">{t("titleHighlight")}</span>
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600">
|
||||
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600 dark:text-slate-400">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
@@ -31,7 +31,7 @@ export default function Roadmap() {
|
||||
<Link
|
||||
key={d.day}
|
||||
href={`/day/${d.day}`}
|
||||
className="card-hover group flex flex-col rounded-2xl border border-slate-100 bg-white p-5 shadow-sm transition-all hover:border-sky-200 hover:shadow-md"
|
||||
className="card-hover group flex flex-col rounded-2xl border border-slate-100 bg-white p-5 shadow-sm transition-all hover:border-sky-200 hover:shadow-md dark:border-slate-800 dark:bg-slate-800 dark:hover:border-sky-700"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
@@ -40,21 +40,21 @@ export default function Roadmap() {
|
||||
DAY {d.day}
|
||||
</div>
|
||||
<span className="text-xl">{d.icon}</span>
|
||||
<h3 className="text-base font-bold text-slate-900 group-hover:text-sky-600">
|
||||
<h3 className="text-base font-bold text-slate-900 group-hover:text-sky-600 dark:text-slate-100 dark:group-hover:text-sky-400">
|
||||
{t(`days.${d.day}.title`)}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mt-3 flex-1 text-sm leading-relaxed text-slate-600">
|
||||
<p className="mt-3 flex-1 text-sm leading-relaxed text-slate-600 dark:text-slate-400">
|
||||
{t(`days.${d.day}.desc`)}
|
||||
</p>
|
||||
<span className="mt-3 inline-flex items-center text-sm font-medium text-sky-600">
|
||||
<span className="mt-3 inline-flex items-center text-sm font-medium text-sky-600 dark:text-sky-400">
|
||||
{t("viewDetails")}
|
||||
</span>
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
href="/ebook"
|
||||
className="card-hover group flex flex-col rounded-2xl border border-slate-100 bg-white p-5 shadow-sm transition-all hover:border-sky-200 hover:shadow-md"
|
||||
className="card-hover group flex flex-col rounded-2xl border border-slate-100 bg-white p-5 shadow-sm transition-all hover:border-sky-200 hover:shadow-md dark:border-slate-800 dark:bg-slate-800 dark:hover:border-sky-700"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-gradient-to-r from-sky-500 to-cyan-500 text-xs font-bold text-white">
|
||||
@@ -73,19 +73,19 @@ export default function Roadmap() {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 overflow-hidden rounded-2xl border border-amber-200 bg-gradient-to-r from-amber-50 to-orange-50 p-8 text-center sm:p-10">
|
||||
<div className="mb-2 text-sm font-semibold text-amber-600">
|
||||
<div className="mt-16 overflow-hidden rounded-2xl border border-amber-200 bg-gradient-to-r from-amber-50 to-orange-50 p-8 text-center dark:border-amber-800 dark:from-amber-900/30 dark:to-orange-900/30 sm:p-10">
|
||||
<div className="mb-2 text-sm font-semibold text-amber-600 dark:text-amber-400">
|
||||
{t("course.tag")}
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-slate-900">
|
||||
{t("course.title")}
|
||||
</h3>
|
||||
<p className="mx-auto mt-3 max-w-xl text-slate-600">
|
||||
<p className="mx-auto mt-3 max-w-xl text-slate-600 dark:text-slate-400">
|
||||
{t("course.desc")}
|
||||
</p>
|
||||
<Link
|
||||
href="/course"
|
||||
className="mt-6 inline-flex items-center gap-2 rounded-full bg-amber-500 px-8 py-3 font-semibold text-white transition-colors hover:bg-amber-600"
|
||||
className="mt-6 inline-flex items-center gap-2 rounded-full bg-amber-500 px-8 py-3 font-semibold text-white transition-colors hover:bg-amber-600 dark:bg-amber-600 dark:hover:bg-amber-500"
|
||||
>
|
||||
{t("course.cta")}
|
||||
</Link>
|
||||
|
||||
47
app/components/ThemeToggle.tsx
Normal file
47
app/components/ThemeToggle.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
"use client";
|
||||
|
||||
import { useTheme } from "../context/ThemeContext";
|
||||
|
||||
export default function ThemeToggle() {
|
||||
const { theme, toggleTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleTheme}
|
||||
className="rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
|
||||
aria-label={theme === "dark" ? "切换到日间模式" : "切换到夜间模式"}
|
||||
title={theme === "dark" ? "日间模式" : "夜间模式"}
|
||||
>
|
||||
{theme === "dark" ? (
|
||||
<svg
|
||||
className="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
className="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -27,16 +27,16 @@ export default function Tools() {
|
||||
{ value: "佣金", key: "affiliate" as const },
|
||||
];
|
||||
return (
|
||||
<section id="tools" className="py-20 sm:py-28">
|
||||
<section id="tools" className="py-20 sm:py-28 dark:bg-slate-950">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<div className="mb-4 inline-flex items-center gap-2 rounded-full border border-sky-200 bg-sky-50 px-4 py-1.5 text-sm font-medium text-sky-700">
|
||||
<div className="mb-4 inline-flex items-center gap-2 rounded-full border border-sky-200 bg-sky-50 px-4 py-1.5 text-sm font-medium text-sky-700 dark:border-sky-800 dark:bg-sky-900/50 dark:text-sky-300">
|
||||
{t("badge")}
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
|
||||
<span className="gradient-text">{t("title")}</span>
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600">
|
||||
<p className="mx-auto mt-4 max-w-2xl text-lg text-slate-600 dark:text-slate-400">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
@@ -45,14 +45,14 @@ export default function Tools() {
|
||||
{categories.map((cat) => (
|
||||
<div
|
||||
key={cat.title}
|
||||
className="card-hover group overflow-visible rounded-2xl border border-slate-100 bg-white shadow-sm"
|
||||
className="card-hover group overflow-visible rounded-2xl border border-slate-100 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900"
|
||||
>
|
||||
<div className={`h-1.5 bg-gradient-to-r ${cat.color}`} />
|
||||
<div className="p-5">
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-2xl">{cat.icon}</span>
|
||||
<h3 className="font-bold text-slate-900">{cat.title}</h3>
|
||||
<h3 className="font-bold text-slate-900 dark:text-slate-100">{cat.title}</h3>
|
||||
</div>
|
||||
<span className={`rounded-full px-2 py-0.5 text-xs font-semibold ${cat.bg} ${cat.text}`}>
|
||||
{cat.tools.length}
|
||||
@@ -65,9 +65,9 @@ export default function Tools() {
|
||||
href={tool.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group/item flex items-center justify-between rounded-lg bg-slate-50 px-3 py-2 text-sm transition-colors hover:bg-slate-100/80 hover:text-sky-600"
|
||||
className="group/item flex items-center justify-between rounded-lg bg-slate-50 px-3 py-2 text-sm transition-colors hover:bg-slate-100/80 hover:text-sky-600 dark:bg-slate-800 dark:hover:bg-slate-700 dark:hover:text-sky-400"
|
||||
>
|
||||
<span className="font-medium text-slate-700">{tool.name}</span>
|
||||
<span className="font-medium text-slate-700 dark:text-slate-300">{tool.name}</span>
|
||||
<span className="relative text-slate-400">
|
||||
{truncateDesc(tool.desc)}
|
||||
{tool.desc && tool.desc.length > 5 && (
|
||||
@@ -88,12 +88,12 @@ export default function Tools() {
|
||||
{toolStats.map((s) => (
|
||||
<div
|
||||
key={s.key}
|
||||
className="rounded-2xl border border-slate-100 bg-white p-5 text-center shadow-sm"
|
||||
className="rounded-2xl border border-slate-100 bg-white p-5 text-center shadow-sm dark:border-slate-800 dark:bg-slate-900"
|
||||
>
|
||||
<div className="text-3xl font-extrabold text-sky-600">
|
||||
<div className="text-3xl font-extrabold text-sky-600 dark:text-sky-400">
|
||||
{s.value}
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-slate-500">{t(`stats.${s.key}`)}</div>
|
||||
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">{t(`stats.${s.key}`)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user