diff --git a/app/community/_content.tsx b/app/community/_content.tsx index c328a225..81640056 100644 --- a/app/community/_content.tsx +++ b/app/community/_content.tsx @@ -3,14 +3,22 @@ import Image from "next/image"; import { useEffect, useMemo, useState } from "react"; import { usePathname, useRouter } from "next/navigation"; -import { Loader2, LogIn, RefreshCw, Send, TrendingUp } from "lucide-react"; +import { Loader2, LogIn, RefreshCw, Send, TrendingUp, Clock, Heart, Zap } from "lucide-react"; import { LocalizedLink } from "@/components/i18n/localized-link"; import { useCommunityFeed } from "@/lib/swr"; import { useUser } from "@/lib/auth/use-user"; import { useI18n } from "@/lib/i18n/context"; -import { SORT_OPTIONS, type SortMethod } from "@/lib/community/leaderboard"; +import { SORT_OPTIONS_DEF, type SortMethod, type SortOptionDef } from "@/lib/community/leaderboard"; import { CommunityStatsCard } from "@/components/community/community-stats-card"; +// Icon mapping for sort options +const SORT_ICONS = { + TrendingUp: TrendingUp, + Clock: Clock, + Heart: Heart, + Zap: Zap, +} as const; + const PAGE_SIZE = 12; function formatDate(iso: string, locale: "zh" | "en"): string { @@ -163,21 +171,27 @@ export function CommunityContent({ {/* Sort Controls */}
- {SORT_OPTIONS.map((option) => ( - - ))} + {SORT_OPTIONS_DEF.map((option) => { + const Icon = SORT_ICONS[option.iconName]; + const label = locale === "zh" ? option.labelZh : option.labelEn; + const description = locale === "zh" ? option.descriptionZh : option.descriptionEn; + + return ( + + ); + })}
{error && ( @@ -358,6 +372,7 @@ export function CommunityContent({ )} + ); diff --git a/app/guides/page.tsx b/app/guides/page.tsx index 93189ebc..114aaef6 100644 --- a/app/guides/page.tsx +++ b/app/guides/page.tsx @@ -3,7 +3,17 @@ import { Header } from "@/components/layout/header"; import { Footer } from "@/components/layout/footer"; import { LocalizedLink } from "@/components/i18n/localized-link"; import { styleGuides } from "@/lib/seo/style-guides"; -import { BookOpen, ArrowRight } from "lucide-react"; +import { BookOpen, ArrowRight, Sparkles, History } from "lucide-react"; + +// Color scheme for each guide +const guideColors: Record = { + neumorphism: "from-rose-500/20 to-orange-500/20", + "minimalist-flat": "from-zinc-400/20 to-slate-500/20", + glassmorphism: "from-blue-500/20 to-purple-500/20", + "neo-brutalism": "from-yellow-400/20 to-pink-500/20", + editorial: "from-stone-400/20 to-amber-500/20", + "cyber-wafuu": "from-red-500/20 to-cyan-500/20", +}; export const metadata: Metadata = { title: "Design Style Guides - StyleKit", @@ -55,54 +65,71 @@ export default function GuidesPage() {
- {guides.map((guide) => ( - -
-
-

- {guide.nameEn} -

- {guide.name !== guide.nameEn && ( -

{guide.name}

- )} + {guides.map((guide) => { + const colorScheme = guideColors[guide.slug] || "from-gray-400/20 to-gray-500/20"; + + return ( + + {/* Gradient header */} +
+
+
+
+

+ {guide.nameEn} +

+ {guide.name !== guide.nameEn && ( +

{guide.name}

+ )} +
+
+ +
+
- -
+ +
+

+ {guide.descriptionEn} +

-

- {guide.descriptionEn} -

+ {/* Influenced by tags */} + {guide.influencedBy && guide.influencedBy.length > 0 && ( +
+ +
+ {guide.influencedBy.map((style) => ( + + {style} + + ))} +
+
+ )} - {guide.influencedBy && guide.influencedBy.length > 0 && ( -
- {guide.influencedBy.map((style) => ( - - {style} - - ))} +
+
+ + + {guide.useCases.length} use case{guide.useCases.length !== 1 ? "s" : ""} + + + + {guide.references.length} reference{guide.references.length !== 1 ? "s" : ""} + +
+
- )} - -
- - {guide.useCases.length} use case - {guide.useCases.length !== 1 ? "s" : ""} - - - - {guide.references.length} reference - {guide.references.length !== 1 ? "s" : ""} - -
- - ))} + + ); + })}
diff --git a/app/recipes/page.tsx b/app/recipes/page.tsx index 7ce98eb0..6d310745 100644 --- a/app/recipes/page.tsx +++ b/app/recipes/page.tsx @@ -37,22 +37,35 @@ export default function RecipesPage() { {/* Hero */} -
-
-

+
+ {/* Decorative gradient */} +
+ +
+
+ + Style + Layout + Animation +
+ +

Design Recipes

-

+

Curated combinations of visual styles, layouts, and animations. Each recipe is optimized for a specific use case and includes reasoning for why it works.

-
- - {allRecipes.length} Recipes + +
+ + + {allRecipes.length} + Recipes - - {allRecipes.filter((r) => r.featured).length} Featured + + + {allRecipes.filter((r) => r.featured).length} + Featured
diff --git a/components/community/community-stats-card.tsx b/components/community/community-stats-card.tsx index 891b99cd..9595a5f0 100644 --- a/components/community/community-stats-card.tsx +++ b/components/community/community-stats-card.tsx @@ -1,4 +1,7 @@ -import { Heart, Eye, Share2, TrendingUp, Clock, Award } from "lucide-react"; +"use client"; + +import { Heart, Eye, TrendingUp, Clock, Award, Users } from "lucide-react"; +import { useI18n } from "@/lib/i18n/context"; export interface CommunityStats { totalSubmissions: number; @@ -17,62 +20,92 @@ interface CommunityStatsCardProps { } export function CommunityStatsCard({ stats }: CommunityStatsCardProps) { + const { locale } = useI18n(); + const isZh = locale === "zh"; + const statItems = [ { - icon: , - label: "Total Styles", + icon: Award, + label: isZh ? "社区风格" : "Community Styles", value: stats.totalSubmissions.toLocaleString(), - color: "bg-blue-500/10 text-blue-600 dark:text-blue-400", + gradient: "from-blue-500/20 to-indigo-500/20", + iconColor: "text-blue-500", }, { - icon: , - label: "Contributors", + icon: Users, + label: isZh ? "贡献者" : "Contributors", value: stats.totalCollaborators.toLocaleString(), - color: "bg-purple-500/10 text-purple-600 dark:text-purple-400", + gradient: "from-purple-500/20 to-pink-500/20", + iconColor: "text-purple-500", }, { - icon: , - label: "This Month", + icon: Clock, + label: isZh ? "本月新增" : "This Month", value: stats.recentSubmissions.toLocaleString(), - color: "bg-green-500/10 text-green-600 dark:text-green-400", + gradient: "from-emerald-500/20 to-teal-500/20", + iconColor: "text-emerald-500", }, ]; return ( -
+
-
- {statItems.map((item, idx) => ( -
- {item.icon} -
-
{item.label}
-
{item.value}
+ {/* Stats Grid */} +
+ {statItems.map((item, idx) => { + const Icon = item.icon; + return ( +
+
+
+ +
+
+
+ {item.label} +
+
+ {item.value} +
+
+
-
- ))} + ); + })}
+ {/* Featured Style */} {stats.topStyle && ( -
-
- - Featured This Month -
-

{stats.topStyle.title}

-
-
by {stats.topStyle.author}
-
-
- - {stats.topStyle.likes} +
+
+ +
+
+ + + {isZh ? "本月精选" : "Featured This Month"} + +
+ +

{stats.topStyle.title}

+ +
+
+ {isZh ? "作者:" : "by "}{stats.topStyle.author}
-
- - {stats.topStyle.views} +
+
+ + {stats.topStyle.likes} +
+
+ + {stats.topStyle.views} +
diff --git a/components/playground/playground-preview.tsx b/components/playground/playground-preview.tsx index e2b2a70b..806c982c 100644 --- a/components/playground/playground-preview.tsx +++ b/components/playground/playground-preview.tsx @@ -5,14 +5,13 @@ import { Loader2, ZoomIn, ZoomOut, - RotateCcw, Maximize2, Minimize2, MousePointer, Eye, - EyeOff, X } from "lucide-react"; +import { useI18n } from "@/lib/i18n/context"; interface PlaygroundPreviewProps { code: string; @@ -175,6 +174,9 @@ export function PlaygroundPreview({ deviceWidth, onElementSelect, }: PlaygroundPreviewProps) { + const { locale } = useI18n(); + const isZh = locale === "zh"; + const [debouncedCode, setDebouncedCode] = useState(code); const [debouncedTokenCss, setDebouncedTokenCss] = useState(tokenCss); const [zoom, setZoom] = useState(1); @@ -278,12 +280,12 @@ export function PlaygroundPreview({ {/* 预览控制栏 */}
- {/* 缩放控制 */} + {/* Zoom Controls */} @@ -291,7 +293,7 @@ export function PlaygroundPreview({ @@ -300,14 +302,14 @@ export function PlaygroundPreview({ onClick={handleZoomIn} disabled={zoom >= ZOOM_LEVELS[ZOOM_LEVELS.length - 1]} className="p-1 text-muted hover:text-foreground disabled:opacity-30 disabled:cursor-not-allowed transition-colors" - title="放大" + title={isZh ? "放大" : "Zoom In"} >
- {/* 元素检查器 */} + {/* Element Inspector */}
- {/* 全屏切换 */} + {/* Fullscreen Toggle */}
- {/* 加载指示器 */} + {/* Loading Indicator */} {loading && (
- 更新中... + {isZh ? "更新中..." : "Updating..."}
)} @@ -408,11 +414,11 @@ export function PlaygroundPreview({
- {/* 检查器模式提示 */} + {/* Inspector Mode Hint */} {inspectorEnabled && (
- 点击元素查看类名 + {isZh ? "点击元素查看类名" : "Click elements to inspect"}
)}
diff --git a/components/recipes/recipe-card.tsx b/components/recipes/recipe-card.tsx index 57360a37..767fac82 100644 --- a/components/recipes/recipe-card.tsx +++ b/components/recipes/recipe-card.tsx @@ -1,6 +1,6 @@ "use client"; -import { ArrowRight, Layers, Layout, Sparkles } from "lucide-react"; +import { ArrowRight, Layers, Layout, Sparkles, Check } from "lucide-react"; import { LocalizedLink } from "@/components/i18n/localized-link"; import { useI18n } from "@/lib/i18n/context"; import type { StyleRecipe } from "@/lib/styles/recipes"; @@ -12,36 +12,49 @@ interface RecipeCardProps { export function RecipeCard({ recipe, variant = "default" }: RecipeCardProps) { const { locale } = useI18n(); + const isZh = locale === "zh"; - const name = locale === "zh" ? recipe.nameZh : recipe.name; - const description = locale === "zh" ? recipe.descriptionZh : recipe.description; - const reasoning = locale === "zh" ? recipe.reasoningZh : recipe.reasoning; + const name = isZh ? recipe.nameZh : recipe.name; + const description = isZh ? recipe.descriptionZh : recipe.description; + const reasoning = isZh ? recipe.reasoningZh : recipe.reasoning; if (variant === "compact") { return ( -
-
+ + {/* Subtle gradient overlay on hover */} +