本页出处
设计风格资料库
内容来自 luckymaomi 的 UI 风格库(复刻版),以 MIT 许可 提供。本站镜像收录,仅作学习参考;这一页的原始地址是 原站页面。
← 回到导览访问原站仓库
返回模板目录
lifestylenatural-organic

食谱烹饪

食谱页面,包含步骤勾选、食材份量调整、分类浏览和营养信息。

独立打开

完整模板源代码

查看 965 行代码
"use client";

export const dynamic = "force-static";

import { useState } from "react";
import {
  Bookmark,
  BookmarkCheck,
  ChefHat,
  Clock,
  Flame,
  Heart,
  Search,
  Star,
  Users,
  UtensilsCrossed,
  X,
} from "@/components/IconShim";
import { TemplateBackLink } from "@/components/templates/TemplateBackLink";

type DishKey =
  | "salmon"
  | "risotto"
  | "caesar"
  | "tiramisu"
  | "tomyum"
  | "wellington"
  | "bruschetta";

function Plate({
  children,
  rim = "#ffffff",
  well = "#f6f1ea",
}: {
  children: React.ReactNode;
  rim?: string;
  well?: string;
}) {
  return (
    <div
      className="relative rounded-full"
      style={{
        width: "72%",
        aspectRatio: "1",
        background: rim,
        boxShadow:
          "0 14px 24px -10px rgba(60,30,10,0.35), inset 0 -3px 6px rgba(0,0,0,0.08)",
      }}
    >
      <div
        className="absolute rounded-full"
        style={{
          inset: "11%",
          background: well,
          boxShadow: "inset 0 2px 6px rgba(0,0,0,0.10)",
        }}
      >
        {children}
      </div>
    </div>
  );
}

function DishIllustration({ dish }: { dish: DishKey }) {
  if (dish === "salmon") {
    return (
      <Plate well="#fdf6ee">
        {/* salmon fillet */}
        <div
          className="absolute"
          style={{
            left: "16%",
            top: "26%",
            width: "56%",
            height: "40%",
            borderRadius: "48% 52% 55% 45% / 60% 55% 45% 40%",
            background:
              "linear-gradient(135deg, #fb923c 0%, #f97316 55%, #ea580c 100%)",
            boxShadow: "inset 0 -4px 6px rgba(154,52,18,0.35)",
          }}
        >
          {[28, 48, 68].map((l) => (
            <div
              key={l}
              className="absolute"
              style={{
                left: `${l}%`,
                top: "12%",
                width: "6%",
                height: "76%",
                borderRadius: "999px",
                background: "rgba(255,247,237,0.65)",
                transform: "rotate(14deg)",
              }}
            />
          ))}
        </div>
        {/* lemon slice */}
        <div
          className="absolute rounded-full"
          style={{
            right: "10%",
            top: "18%",
            width: "22%",
            aspectRatio: "1",
            background:
              "radial-gradient(circle, #fef9c3 0 55%, #facc15 56% 100%)",
            boxShadow: "0 2px 3px rgba(0,0,0,0.12)",
          }}
        />
        {/* asparagus */}
        {[0, 1, 2].map((i) => (
          <div
            key={i}
            className="absolute"
            style={{
              left: `${20 + i * 8}%`,
              bottom: "12%",
              width: "5%",
              height: "26%",
              borderRadius: "999px",
              background: "linear-gradient(180deg, #4d7c0f, #65a30d)",
              transform: `rotate(${18 + i * 6}deg)`,
            }}
          />
        ))}
      </Plate>
    );
  }

  if (dish === "risotto") {
    return (
      <Plate well="#f3ede2">
        {/* creamy rice mound */}
        <div
          className="absolute"
          style={{
            left: "12%",
            top: "22%",
            width: "76%",
            height: "58%",
            borderRadius: "50% 50% 46% 54% / 62% 58% 42% 38%",
            background:
              "radial-gradient(circle at 35% 30%, #fef3c7 0 40%, #fde68a 41% 75%, #fbbf24 76% 100%)",
            boxShadow: "inset 0 -5px 8px rgba(180,83,9,0.25)",
          }}
        />
        {/* mushroom slices */}
        {[
          { l: 26, t: 30, r: -18 },
          { l: 52, t: 26, r: 12 },
          { l: 40, t: 48, r: 30 },
          { l: 62, t: 50, r: -8 },
        ].map((m, i) => (
          <div
            key={i}
            className="absolute"
            style={{
              left: `${m.l}%`,
              top: `${m.t}%`,
              width: "16%",
              height: "9%",
              borderRadius: "999px 999px 30% 30% / 999px 999px 40% 40%",
              background: "linear-gradient(180deg, #92400e, #b45309)",
              transform: `rotate(${m.r}deg)`,
            }}
          />
        ))}
        {/* parsley flecks */}
        {[34, 50, 66].map((l, i) => (
          <div
            key={l}
            className="absolute rounded-full"
            style={{
              left: `${l}%`,
              top: `${36 + i * 8}%`,
              width: "4%",
              aspectRatio: "1",
              background: "#65a30d",
            }}
          />
        ))}
      </Plate>
    );
  }

  if (dish === "caesar") {
    return (
      <Plate well="#f0f6ec">
        {/* lettuce leaves */}
        {[
          { l: 12, t: 24, r: -24, c: "#86efac" },
          { l: 38, t: 16, r: 8, c: "#4ade80" },
          { l: 56, t: 30, r: 34, c: "#86efac" },
          { l: 20, t: 46, r: 16, c: "#4ade80" },
          { l: 46, t: 48, r: -14, c: "#a3e635" },
        ].map((leaf, i) => (
          <div
            key={i}
            className="absolute"
            style={{
              left: `${leaf.l}%`,
              top: `${leaf.t}%`,
              width: "34%",
              height: "26%",
              borderRadius: "80% 20% 75% 25% / 65% 35% 70% 30%",
              background: `linear-gradient(135deg, ${leaf.c}, #22c55e)`,
              transform: `rotate(${leaf.r}deg)`,
              boxShadow: "0 1px 2px rgba(0,0,0,0.10)",
            }}
          />
        ))}
        {/* croutons */}
        {[
          { l: 30, t: 34 },
          { l: 56, t: 52 },
          { l: 44, t: 24 },
        ].map((c, i) => (
          <div
            key={i}
            className="absolute"
            style={{
              left: `${c.l}%`,
              top: `${c.t}%`,
              width: "11%",
              aspectRatio: "1",
              borderRadius: "22%",
              background: "linear-gradient(135deg, #fcd34d, #d97706)",
              transform: `rotate(${i * 20}deg)`,
            }}
          />
        ))}
        {/* parmesan shavings */}
        {[
          { l: 24, t: 60 },
          { l: 60, t: 36 },
        ].map((p, i) => (
          <div
            key={i}
            className="absolute"
            style={{
              left: `${p.l}%`,
              top: `${p.t}%`,
              width: "14%",
              height: "5%",
              borderRadius: "999px",
              background: "#fef9c3",
              transform: `rotate(${-20 + i * 40}deg)`,
            }}
          />
        ))}
      </Plate>
    );
  }

  if (dish === "tiramisu") {
    return (
      <Plate rim="#fdf2f8" well="#fbe7ef">
        {/* layered slice */}
        <div
          className="absolute overflow-hidden"
          style={{
            left: "22%",
            top: "22%",
            width: "56%",
            height: "52%",
            borderRadius: "10% 10% 14% 14%",
            boxShadow: "0 5px 10px rgba(80,40,20,0.30)",
          }}
        >
          {["#78350f", "#fef3c7", "#a16207", "#fef3c7", "#78350f"].map(
            (c, i) => (
              <div
                key={i}
                className="absolute left-0 w-full"
                style={{ top: `${i * 20}%`, height: "20%", background: c }}
              />
            ),
          )}
          {/* cocoa dusting */}
          <div
            className="absolute left-0 top-0 w-full"
            style={{
              height: "20%",
              background:
                "repeating-linear-gradient(90deg, #451a03 0 6px, #78350f 6px 12px)",
            }}
          />
        </div>
        {/* coffee bean garnish */}
        <div
          className="absolute"
          style={{
            right: "16%",
            bottom: "14%",
            width: "12%",
            height: "8%",
            borderRadius: "999px",
            background: "#431407",
            transform: "rotate(-24deg)",
          }}
        />
      </Plate>
    );
  }

  if (dish === "tomyum") {
    return (
      <Plate rim="#fff7ed" well="#fff7ed">
        {/* bowl */}
        <div
          className="absolute overflow-hidden"
          style={{
            left: "12%",
            top: "22%",
            width: "76%",
            height: "58%",
            borderRadius: "50% 50% 46% 46% / 40% 40% 62% 62%",
            background:
              "linear-gradient(180deg, #fecaca 0 8%, #ef4444 8% 100%)",
            boxShadow: "inset 0 -6px 10px rgba(127,29,29,0.4)",
          }}
        >
          {/* broth surface */}
          <div
            className="absolute left-0 top-0 w-full"
            style={{
              height: "42%",
              background: "linear-gradient(180deg, #fb923c, #ea580c)",
            }}
          />
          {/* shrimp */}
          {[24, 56].map((l, i) => (
            <div
              key={l}
              className="absolute"
              style={{
                left: `${l}%`,
                top: "10%",
                width: "22%",
                height: "22%",
                borderRadius: "999px 999px 999px 20%",
                background: "linear-gradient(135deg, #fda4af, #f43f5e)",
                transform: `rotate(${i === 0 ? -20 : 25}deg)`,
              }}
            />
          ))}
          {/* lime leaf */}
          <div
            className="absolute"
            style={{
              left: "44%",
              top: "22%",
              width: "16%",
              height: "10%",
              borderRadius: "80% 20% 80% 20%",
              background: "#16a34a",
              transform: "rotate(30deg)",
            }}
          />
        </div>
        {/* steam wisps */}
        {[30, 48, 64].map((l, i) => (
          <div
            key={l}
            className="absolute motion-safe:animate-pulse"
            style={{
              left: `${l}%`,
              top: "8%",
              width: "4%",
              height: "14%",
              borderRadius: "999px",
              background: "rgba(255,255,255,0.75)",
              animationDelay: `${i * 0.4}s`,
            }}
          />
        ))}
      </Plate>
    );
  }

  if (dish === "wellington") {
    return (
      <Plate well="#f5efe6">
        {/* pastry loaf */}
        <div
          className="absolute overflow-hidden"
          style={{
            left: "14%",
            top: "28%",
            width: "72%",
            height: "42%",
            borderRadius: "48% 48% 22% 22% / 90% 90% 20% 20%",
            background:
              "linear-gradient(180deg, #d97706 0%, #b45309 60%, #92400e 100%)",
            boxShadow:
              "inset 0 -5px 8px rgba(69,26,3,0.40), 0 4px 8px rgba(69,26,3,0.30)",
          }}
        >
          {/* pastry lattice scoring */}
          <div
            className="absolute inset-0"
            style={{
              background:
                "repeating-linear-gradient(65deg, transparent 0 12px, rgba(254,243,199,0.45) 12px 15px)",
            }}
          />
        </div>
        {/* cut face showing beef center */}
        <div
          className="absolute"
          style={{
            left: "14%",
            top: "44%",
            width: "20%",
            height: "26%",
            borderRadius: "40% 20% 20% 40% / 50% 30% 30% 50%",
            background:
              "radial-gradient(circle at 50% 50%, #be123c 0 45%, #9f1239 46% 70%, #fbbf24 71% 100%)",
          }}
        />
        {/* rosemary sprig */}
        <div
          className="absolute"
          style={{
            right: "14%",
            bottom: "14%",
            width: "22%",
            height: "5%",
            borderRadius: "999px",
            background: "linear-gradient(90deg, #14532d, #166534)",
            transform: "rotate(-18deg)",
          }}
        />
      </Plate>
    );
  }

  // bruschetta
  return (
    <Plate rim="#fefce8" well="#faf5e4">
      {[
        { l: 10, t: 26, r: -14 },
        { l: 44, t: 40, r: 10 },
      ].map((slice, i) => (
        <div
          key={i}
          className="absolute"
          style={{
            left: `${slice.l}%`,
            top: `${slice.t}%`,
            width: "46%",
            height: "32%",
            transform: `rotate(${slice.r}deg)`,
          }}
        >
          {/* toasted bread */}
          <div
            className="absolute inset-0"
            style={{
              borderRadius: "45% 55% 50% 50% / 60% 60% 40% 40%",
              background: "linear-gradient(135deg, #fbbf24, #d97706)",
              boxShadow: "0 3px 5px rgba(120,53,15,0.30)",
            }}
          />
          {/* tomato topping */}
          {[
            { l: 14, t: 12 },
            { l: 44, t: 8 },
            { l: 30, t: 38 },
            { l: 60, t: 34 },
          ].map((tm, j) => (
            <div
              key={j}
              className="absolute rounded-full"
              style={{
                left: `${tm.l}%`,
                top: `${tm.t}%`,
                width: "24%",
                aspectRatio: "1",
                background:
                  "radial-gradient(circle at 35% 30%, #f87171, #dc2626)",
              }}
            />
          ))}
          {/* basil */}
          <div
            className="absolute"
            style={{
              left: "38%",
              top: "22%",
              width: "22%",
              height: "26%",
              borderRadius: "80% 20% 80% 20%",
              background: "#16a34a",
              transform: "rotate(24deg)",
            }}
          />
        </div>
      ))}
    </Plate>
  );
}

const categories = [
  { name: "All Recipes", count: 6 },
  { name: "Appetizers", count: 1 },
  { name: "Main Courses", count: 2 },
  { name: "Desserts", count: 1 },
  { name: "Soups", count: 1 },
  { name: "Salads", count: 1 },
  { name: "Beverages", count: 0 },
];

const featuredRecipe = {
  title: "Pan-Seared Salmon with Lemon Butter",
  description:
    "A perfectly crispy salmon fillet drizzled with a luxurious lemon butter sauce, served alongside roasted asparagus and baby potatoes.",
  cookTime: "35 min",
  servings: 4,
  difficulty: "Medium",
  rating: 4.9,
  reviews: 328,
  gradient: "bg-gradient-to-br from-orange-400 to-rose-500",
  dish: "salmon" as DishKey,
};

const recipes: {
  title: string;
  cookTime: string;
  difficulty: string;
  rating: number;
  category: string;
  gradient: string;
  dish: DishKey;
}[] = [
  {
    title: "Mushroom Risotto",
    cookTime: "45 min",
    difficulty: "Medium",
    rating: 4.7,
    category: "Main Courses",
    gradient: "bg-gradient-to-br from-amber-300 to-yellow-500",
    dish: "risotto",
  },
  {
    title: "Caesar Salad",
    cookTime: "15 min",
    difficulty: "Easy",
    rating: 4.5,
    category: "Salads",
    gradient: "bg-gradient-to-br from-green-300 to-emerald-500",
    dish: "caesar",
  },
  {
    title: "Tiramisu",
    cookTime: "30 min",
    difficulty: "Medium",
    rating: 4.8,
    category: "Desserts",
    gradient: "bg-gradient-to-br from-amber-600 to-yellow-800",
    dish: "tiramisu",
  },
  {
    title: "Tom Yum Soup",
    cookTime: "25 min",
    difficulty: "Easy",
    rating: 4.6,
    category: "Soups",
    gradient: "bg-gradient-to-br from-red-400 to-orange-500",
    dish: "tomyum",
  },
  {
    title: "Beef Wellington",
    cookTime: "90 min",
    difficulty: "Hard",
    rating: 4.9,
    category: "Main Courses",
    gradient: "bg-gradient-to-br from-red-700 to-rose-900",
    dish: "wellington",
  },
  {
    title: "Bruschetta",
    cookTime: "10 min",
    difficulty: "Easy",
    rating: 4.4,
    category: "Appetizers",
    gradient: "bg-gradient-to-br from-rose-300 to-red-400",
    dish: "bruschetta",
  },
];

const detailRecipe = {
  title: "Pan-Seared Salmon with Lemon Butter",
  ingredients: [
    "4 salmon fillets (6 oz each)",
    "2 tbsp olive oil",
    "3 tbsp unsalted butter",
    "2 cloves garlic, minced",
    "Juice of 1 lemon",
    "1 tbsp fresh dill, chopped",
    "Salt and pepper to taste",
    "1 lb asparagus, trimmed",
    "1 lb baby potatoes, halved",
  ],
  steps: [
    "Pat salmon fillets dry and season generously with salt and pepper on both sides.",
    "Heat olive oil in a large skillet over medium-high heat until shimmering.",
    "Place salmon skin-side up and cook for 4 minutes until a golden crust forms.",
    "Flip carefully and cook for another 3 minutes for medium doneness.",
    "Remove salmon from pan and set aside on a warm plate.",
    "In the same pan, reduce heat to medium and add butter and garlic.",
    "Once butter is melted and garlic is fragrant, add lemon juice and dill.",
    "Drizzle the lemon butter sauce over the salmon and serve with roasted vegetables.",
  ],
};

export default function RecipeCookingTemplate() {
  const [activeCategory, setActiveCategory] = useState("All Recipes");
  const [savedRecipes, setSavedRecipes] = useState<string[]>([]);
  const [showDetail, setShowDetail] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const [sidebarOpen, setSidebarOpen] = useState(false);
  const [showSavedOnly, setShowSavedOnly] = useState(false);

  const toggleSave = (title: string) => {
    setSavedRecipes((prev) =>
      prev.includes(title) ? prev.filter((r) => r !== title) : [...prev, title],
    );
  };

  const difficultyColor = (d: string) => {
    if (d === "Easy") return "text-green-600 bg-green-50";
    if (d === "Medium") return "text-amber-600 bg-amber-50";
    return "text-red-600 bg-red-50";
  };

  const normalizedSearch = searchQuery.trim().toLocaleLowerCase();
  const visibleRecipes = recipes.filter((recipe) => {
    const matchesCategory =
      activeCategory === "All Recipes" || recipe.category === activeCategory;
    const matchesSearch =
      !normalizedSearch ||
      recipe.title.toLocaleLowerCase().includes(normalizedSearch);
    const matchesSaved =
      !showSavedOnly || savedRecipes.includes(recipe.title);
    return matchesCategory && matchesSearch && matchesSaved;
  });
  const showFeatured =
    activeCategory === "All Recipes" &&
    !normalizedSearch &&
    !showSavedOnly &&
    !showDetail;

  return (
    <div className="min-h-screen bg-[#fffbf7] text-gray-900">
      {/* Header */}
      <header className="border border-gray-200/60 bg-white/80 backdrop-blur-sm sticky top-0 z-30">
        <div className="max-w-7xl mx-auto px-4 md:px-8 py-4 flex items-center justify-between">
          <div className="flex items-center gap-3">
            <div className="w-9 h-9 rounded-xl bg-orange-500 flex items-center justify-center">
              <ChefHat className="w-5 h-5 text-white" />
            </div>
            <span className="font-bold text-lg">CookBook</span>
          </div>
          <div className="hidden md:flex items-center gap-2 bg-gray-100 rounded-lg px-3 py-2 w-72">
            <Search className="w-4 h-4 text-gray-400" />
            <input
              type="text"
              placeholder="Search recipes..."
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              className="bg-transparent text-sm outline-none w-full"
            />
          </div>
          <div className="flex items-center gap-4">
            <button
              onClick={() => setSidebarOpen(!sidebarOpen)}
              className="md:hidden p-2 hover:bg-gray-100 rounded-lg"
              aria-label="Toggle categories"
            >
              <UtensilsCrossed className="w-5 h-5" />
            </button>
            <button
              onClick={() => setShowSavedOnly((current) => !current)}
              className={`relative rounded-lg p-2 transition-colors ${
                showSavedOnly ? "bg-orange-100 text-orange-700" : "hover:bg-gray-100"
              }`}
              aria-label={showSavedOnly ? "Show all recipes" : "Show saved recipes"}
              aria-pressed={showSavedOnly}
            >
              <Bookmark className="w-5 h-5" />
              {savedRecipes.length > 0 && (
                <span className="absolute -top-1 -right-1 w-4 h-4 bg-orange-500 text-white text-[10px] font-bold flex items-center justify-center rounded-full">
                  {savedRecipes.length}
                </span>
              )}
            </button>
          </div>
        </div>
      </header>

      <div className="max-w-7xl mx-auto px-4 md:px-8 py-8">
        <div className="flex gap-8">
          {/* Sidebar */}
          {sidebarOpen && (
            <button
              onClick={() => setSidebarOpen(false)}
              className="fixed inset-0 z-30 bg-black/30 md:hidden"
              aria-label="Close categories"
            />
          )}
          <aside
            className={`fixed inset-y-0 left-0 z-40 w-64 shrink-0 bg-white md:static md:z-auto md:block md:w-52 md:bg-transparent ${
              sidebarOpen ? "block" : "hidden md:block"
            }`}
          >
            <div className="p-6 md:p-0">
              <div className="flex items-center justify-between md:hidden mb-6">
                <h3 className="font-semibold">Categories</h3>
                <button
                  onClick={() => setSidebarOpen(false)}
                  className="rounded-lg p-1 hover:bg-gray-100"
                  aria-label="Close categories"
                >
                  <X className="w-5 h-5" />
                </button>
              </div>
              <h3 className="hidden md:block text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">
                Categories
              </h3>
              <nav className="space-y-1">
                {categories.map((cat) => (
                  <button
                    key={cat.name}
                    onClick={() => {
                      setActiveCategory(cat.name);
                      setSidebarOpen(false);
                    }}
                    className={`w-full flex items-center justify-between px-3 py-2.5 rounded-lg text-sm transition-colors ${
                      activeCategory === cat.name
                        ? "bg-orange-50 text-orange-700 font-medium"
                        : "text-gray-600 hover:bg-gray-50"
                    }`}
                  >
                    <span>{cat.name}</span>
                    <span className="text-xs text-gray-400">{cat.count}</span>
                  </button>
                ))}
              </nav>
            </div>
          </aside>

          {/* Main Content */}
          <main className="flex-1 min-w-0">
            <div className="relative mb-6 md:hidden">
              <Search className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
              <input
                type="search"
                placeholder="Search recipes..."
                value={searchQuery}
                onChange={(event) => setSearchQuery(event.target.value)}
                className="w-full rounded-lg border border-gray-200 bg-white py-2.5 pl-10 pr-4 text-sm outline-none focus:border-orange-400"
              />
            </div>

            {/* Featured Recipe */}
            {showFeatured && (
              <section className="mb-10">
                <button
                  type="button"
                  className={`relative w-full cursor-pointer overflow-hidden rounded-2xl p-8 text-left md:p-12 ${featuredRecipe.gradient}`}
                  onClick={() => setShowDetail(true)}
                >
                  <div className="absolute top-4 right-4 bg-white/20 backdrop-blur-sm rounded-full px-3 py-1 text-white text-sm font-medium flex items-center gap-1">
                    <Star className="w-4 h-4 fill-white" />
                    {featuredRecipe.rating}
                  </div>
                  <div className="hidden lg:flex absolute inset-y-0 right-8 w-64 items-center justify-center pointer-events-none">
                    <DishIllustration dish={featuredRecipe.dish} />
                  </div>
                  <div className="max-w-xl">
                    <span className="inline-block px-3 py-1 bg-white/20 backdrop-blur-sm rounded-full text-white text-xs font-medium mb-4">
                      Featured Recipe
                    </span>
                    <h2 className="text-2xl md:text-3xl font-bold text-white mb-3">
                      {featuredRecipe.title}
                    </h2>
                    <p className="text-white/80 text-sm md:text-base mb-6 leading-relaxed">
                      {featuredRecipe.description}
                    </p>
                    <div className="flex flex-wrap items-center gap-4 text-white/90 text-sm">
                      <span className="flex items-center gap-1.5">
                        <Clock className="w-4 h-4" />
                        {featuredRecipe.cookTime}
                      </span>
                      <span className="flex items-center gap-1.5">
                        <Users className="w-4 h-4" />
                        {featuredRecipe.servings} servings
                      </span>
                      <span className="flex items-center gap-1.5">
                        <Flame className="w-4 h-4" />
                        {featuredRecipe.difficulty}
                      </span>
                    </div>
                  </div>
                </button>
              </section>
            )}

            {/* Recipe Detail View */}
            {showDetail && (
              <section className="mb-10">
                <button
                  onClick={() => setShowDetail(false)}
                  className="mb-6 text-sm text-gray-500 underline underline-offset-4 hover:text-gray-700"
                >
                  Back to recipes
                </button>
                <div
                  className={`rounded-2xl overflow-hidden ${featuredRecipe.gradient} h-48 md:h-64 flex items-center justify-center mb-8`}
                >
                  <div className="h-full py-4 flex items-center justify-center aspect-square">
                    <DishIllustration dish={featuredRecipe.dish} />
                  </div>
                </div>
                <div className="flex flex-wrap items-center gap-3 mb-6">
                  <h1 className="text-2xl md:text-3xl font-bold">
                    {detailRecipe.title}
                  </h1>
                  <button
                    onClick={() => toggleSave(detailRecipe.title)}
                    className="p-2 hover:bg-gray-100 rounded-lg"
                    aria-label="Save recipe"
                  >
                    {savedRecipes.includes(detailRecipe.title) ? (
                      <BookmarkCheck className="w-5 h-5 text-orange-500 fill-orange-500" />
                    ) : (
                      <Bookmark className="w-5 h-5" />
                    )}
                  </button>
                </div>
                <div className="grid md:grid-cols-2 gap-8">
                  <div>
                    <h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
                      <UtensilsCrossed className="w-5 h-5 text-orange-500" />
                      Ingredients
                    </h3>
                    <ul className="space-y-2.5">
                      {detailRecipe.ingredients.map((ing, i) => (
                        <li
                          key={i}
                          className="flex items-center gap-3 text-sm text-gray-700"
                        >
                          <div className="w-2 h-2 bg-orange-400 rounded-full shrink-0" />
                          {ing}
                        </li>
                      ))}
                    </ul>
                  </div>
                  <div>
                    <h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
                      <Flame className="w-5 h-5 text-orange-500" />
                      Instructions
                    </h3>
                    <ol className="space-y-4">
                      {detailRecipe.steps.map((step, i) => (
                        <li
                          key={i}
                          className="flex gap-3 text-sm text-gray-700"
                        >
                          <span className="w-6 h-6 bg-orange-100 text-orange-600 rounded-full flex items-center justify-center text-xs font-bold shrink-0">
                            {i + 1}
                          </span>
                          <span className="leading-relaxed">{step}</span>
                        </li>
                      ))}
                    </ol>
                  </div>
                </div>
              </section>
            )}

            {/* Recipe Grid */}
            {!showDetail && (
              <section>
                <div className="flex items-center justify-between mb-6">
                  <h2 className="text-xl font-bold">
                    {activeCategory === "All Recipes"
                      ? "All Recipes"
                      : activeCategory}
                  </h2>
                  <span className="text-sm text-gray-500">
                    {visibleRecipes.length} recipes
                  </span>
                </div>
                {visibleRecipes.length === 0 ? (
                  <div className="rounded-xl bg-white p-8 text-center text-sm text-gray-500">
                    No matching recipes
                  </div>
                ) : (
                <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
                  {visibleRecipes.map((recipe) => (
                    <div
                      key={recipe.title}
                      className="bg-white rounded-xl border border-gray-100 overflow-hidden hover:shadow-lg transition-shadow group"
                    >
                      <div
                        className={`aspect-[4/3] ${recipe.gradient} flex items-center justify-center relative`}
                      >
                        <div className="h-full py-3 flex items-center justify-center aspect-square">
                          <DishIllustration dish={recipe.dish} />
                        </div>
                        <button
                          onClick={() => toggleSave(recipe.title)}
                          className="absolute top-3 right-3 p-2 bg-white/20 backdrop-blur-sm rounded-full hover:bg-white/40 transition-colors"
                          aria-label="Save recipe"
                        >
                          {savedRecipes.includes(recipe.title) ? (
                            <Heart className="w-4 h-4 text-white fill-white" />
                          ) : (
                            <Heart className="w-4 h-4 text-white" />
                          )}
                        </button>
                      </div>
                      <div className="p-4">
                        <div className="flex items-center gap-2 mb-2">
                          <span className="text-xs text-gray-400">
                            {recipe.category}
                          </span>
                          <span
                            className={`text-xs px-2 py-0.5 rounded-full font-medium ${difficultyColor(recipe.difficulty)}`}
                          >
                            {recipe.difficulty}
                          </span>
                        </div>
                        <h3 className="font-semibold mb-3 group-hover:text-orange-600 transition-colors">
                          {recipe.title}
                        </h3>
                        <div className="flex items-center justify-between text-sm text-gray-500">
                          <span className="flex items-center gap-1">
                            <Clock className="w-3.5 h-3.5" />
                            {recipe.cookTime}
                          </span>
                          <span className="flex items-center gap-1">
                            <Star className="w-3.5 h-3.5 text-amber-400 fill-amber-400" />
                            {recipe.rating}
                          </span>
                        </div>
                      </div>
                    </div>
                  ))}
                </div>
                )}
              </section>
            )}
          </main>
        </div>
      </div>

      <TemplateBackLink />
    </div>
  );
}

查看关联风格