Spaces:
Running
Running
Simeon Garratt
feat: redesign landing page with floating tiles, hero pillars, scoring transparency
7b22979 | "use client"; | |
| import { motion } from "framer-motion"; | |
| import { Check, X } from "lucide-react"; | |
| import { useCosmeticContext } from "@/components/cosmetics/CosmeticProvider"; | |
| type AnswerState = "idle" | "correct" | "wrong" | "disabled"; | |
| interface AnswerButtonProps { | |
| text: string; | |
| state: AnswerState; | |
| onClick: () => void; | |
| index: number; | |
| showBadge?: boolean; | |
| } | |
| const LETTER_LABELS = ["A", "B", "C", "D"]; | |
| const stateStyles: Record<AnswerState, string> = { | |
| idle: "glass-card border-white/20 hover:border-magenta/50 cursor-pointer", | |
| correct: "border-green-400 bg-green-500/25 text-white shadow-[0_0_20px_rgba(34,197,94,0.4),0_0_40px_rgba(34,197,94,0.15)]", | |
| wrong: "border-red-500 bg-red-500/25 text-red-300 animate-shake shadow-[0_0_20px_rgba(239,68,68,0.4)]", | |
| disabled: "border-white/10 opacity-40 cursor-default", | |
| }; | |
| const badgeStyles: Record<AnswerState, string> = { | |
| idle: "bg-white/10", | |
| correct: "bg-green-500 text-white", | |
| wrong: "bg-red-500 text-white", | |
| disabled: "bg-white/5", | |
| }; | |
| export function AnswerButton({ text, state, onClick, index, showBadge = true }: AnswerButtonProps) { | |
| const { equipped } = useCosmeticContext(); | |
| const hasAnswerCosmetic = equipped.answer_style !== null; | |
| // Apply cosmetic border/hover overrides only when idle and a cosmetic is equipped | |
| const cosmeticIdleStyle: React.CSSProperties | undefined = | |
| state === "idle" && hasAnswerCosmetic | |
| ? { borderColor: "var(--cosmetic-answer-border)" } | |
| : undefined; | |
| const cosmeticHoverStyle = | |
| state === "idle" && hasAnswerCosmetic | |
| ? { | |
| borderColor: "var(--cosmetic-answer-hover)", | |
| boxShadow: `0 0 20px var(--cosmetic-answer-hover)`, | |
| } | |
| : undefined; | |
| return ( | |
| <motion.button | |
| initial={{ opacity: 0, x: -10 }} | |
| animate={ | |
| state === "correct" | |
| ? { opacity: 1, x: 0, scale: [1, 1.03, 1] } | |
| : state === "wrong" | |
| ? { opacity: 1, x: 0 } | |
| : { opacity: 1, x: 0 } | |
| } | |
| transition={ | |
| state === "correct" | |
| ? { scale: { duration: 0.3, ease: "easeOut" }, delay: index * 0.05 } | |
| : { delay: index * 0.05 } | |
| } | |
| whileTap={state === "idle" ? { scale: 0.97 } : undefined} | |
| whileHover={cosmeticHoverStyle} | |
| className={`w-full text-left px-5 py-4 rounded-xl border-2 transition-all duration-200 ${stateStyles[state]}`} | |
| style={cosmeticIdleStyle} | |
| onClick={state === "idle" ? onClick : undefined} | |
| disabled={state !== "idle"} | |
| > | |
| <span className="inline-flex items-center gap-3"> | |
| {showBadge && ( | |
| <span className={`flex-shrink-0 w-7 h-7 flex items-center justify-center rounded-lg text-xs font-bold transition-colors duration-200 ${badgeStyles[state]}`}> | |
| {state === "correct" ? <Check size={14} strokeWidth={3} /> : | |
| state === "wrong" ? <X size={14} strokeWidth={3} /> : | |
| LETTER_LABELS[index] ?? ""} | |
| </span> | |
| )} | |
| <span className="font-medium">{text}</span> | |
| </span> | |
| </motion.button> | |
| ); | |
| } | |