提示词
使用 React、Tailwind CSS 和 Framer Motion (motion/react) 构建一个现代化的交互式 hero section。请确保严格遵循以下架构和样式说明:
1. 字体与全局动画
从 Google Fonts 导入 Inter 字体。
在 CSS 设置中,将 Tailwind 配置为默认使用该字体(--font-sans: 'Inter', ...)。
在 CSS 中创建一个名为 blink 的 keyframe animation,用于打字机光标:
code
CSS
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.animate-blink { animation: blink 1s step-end infinite; }
2. 通用页面结构
将整个应用包裹在 container div 中,并使用以下 classes:relative bg-white text-neutral-900 font-sans selection:bg-[#EAECE9] selection:text-[#1C2E1E] antialiased overflow-x-hidden flex flex-col lg:block lg:min-h-screen。
3. 背景视频组件(带原生 Scrubbing)
Container element:添加一个包含背景视频的 div,并使用 classes:order-last lg:order-none relative lg:absolute lg:inset-0 lg:z-0 overflow-hidden pointer-events-none w-full aspect-square md:aspect-video lg:aspect-auto lg:h-full bg-neutral-50 lg:bg-transparent。
Video element:使用带有 muted、playsInline、preload="auto" 的 <video>。
Video Source URL:https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260601_110537_3a579fa0-7bbc-4d94-9d25-0e816c7840f5.mp4
Classes:w-full h-full object-cover object-right lg:object-right-bottom。
通过 useEffect hooks 实现 Scrubbing/Playback Logic:
Desktop Mouse Scrubbing Hook:监听 window mousemove event。如果 window.innerWidth < 1024,则忽略(禁用 scrubbing)。存储鼠标的“previous X”坐标,用于计算它与“current X”的 delta。根据 (delta / window.innerWidth) * 0.8 * video.duration 更新目标 scrub time。将时间限制在 0 与 duration 之间。设置 video.currentTime = targetTime。绑定 seeked event listener,确保逐帧平滑跟踪。
Mobile Autoplay Hook:由于移动端 frames 禁用了 scrubbing,请为宽度 < 1024 的屏幕触发正常播放:video.autoplay = true 和 video.play()。
4. 交互式 Navbar
Header wrapper:将 Navbar 包裹在 <header className="fixed top-0 inset-x-0 z-10 px-5 sm:px-8 py-4 sm:py-5 flex flex-row justify-between items-center bg-transparent"> 中。
Logo(Left side):使用 gap-3 的 flex row。
Text:Mainframe®(使用 ® symbol)。Classes:text-[21px] sm:text-[26px] tracking-tight text-black font-medium select-none。
紧邻文字右侧的 Icon block:一个星号 ✱。Classes:text-[25px] sm:text-[30px] text-black select-none tracking-[-0.02em] font-medium leading-none mb-1。
Desktop Nav Links(Center):使用 flex row、hidden md:flex、text-[23px] text-black。Links 为“Labs”“Studio”“Openings”“Shop”,使用 <span className="opacity-40">, </span> dividers 分隔。Hover states 应使用 hover:opacity-60 transition-opacity。
Desktop CTA(Right):在移动端隐藏。添加一个文本为“Get in touch”的 link,并使用 text-[23px] text-black underline underline-offset-2 hover:opacity-60 transition-opacity。
Mobile Menu Logic:
在 md 以下显示 Hamburger <button>。包含三个 w-6 h-[2px] bg-black spans。
将其连接到 local state isMobileMenuOpen。打开时,将 burger 动画变形为“X”(top bar rotate-45 translate-y-[7px]、middle bar opacity-0、bottom bar -rotate-45 -translate-y-[7px])。所有 spans 都需要 transition-all duration-300。
创建一个在 Desktop 隐藏的全屏 Mobile Navigation Overlay div。使用 fixed inset-0 z-[9] 和 bg-white/95 backdrop-blur-sm。当 isMobileMenuOpen 为 true 时应用 opacity-100 pointer-events-auto;否则应用 opacity-0 pointer-events-none。
5. 内容布局 Container
在背景视频下方并相对于它,添加内容分组层:<div className="relative z-10 flex flex-col order-first lg:order-none w-full bg-white lg:bg-transparent pb-8 lg:pb-0 lg:min-h-screen">
在其中添加整体布局引擎:<main id="spade-hero" className="w-full max-w-7xl mx-auto px-6 py-12 flex-1 flex flex-col justify-center">
6. Typewriter Hook 和 Headline
实现自定义 React hook:useTypewriter(text, speed = 38, startDelay = 600)。它使用 setTimeout 和 setInterval,通过逐个 slice 迭代构建 string。它必须返回一个 object:{ displayed: string, done: boolean }。
使用 string "we'd love tonhear from you!" 运行该 hook。
将 headline 安全地包裹在一个配置为 drop-in 的 motion.div 中(initial: opacity: 0, y: 20, animate: opacity: 1, y: 0, transition duration 0.6)。
在 <h1 className="text-5xl md:text-6xl lg:text-[76px] font-normal tracking-tight text-black leading-[1.08] mb-8 select-none w-full whitespace-pre-wrap"> 中渲染 hook text。
输入过程中(!done),在 displayed text string 末尾输出一个 <span className="inline-block w-[2px] h-[1.1em] bg-black align-middle ml-[2px] animate-blink" /> cursor。
7. 次要描述文字
另一个 motion.div(相对 headline 延迟 0.1s)。
Content:一个 <p> tag,内容为:Whether you have questions, feedback, <br /> drop us a message and we'll get back to you as soon as possible.
Classes:text-lg md:text-xl text-[#5A635A] leading-relaxed font-normal mb-14 max-w-2xl。
8. 交互式 Multi-Select Service Pills
使用 setServices 跟踪 array ["Brand", "Digital", "Campaign", "Other"]。
Prompt Title:“What sort of service?”(text-2xl font-medium tracking-tight mb-2)。Subtitle:“Select all that apply”(opacity-85 text-[#738273] mb-8)。
原生遍历 options,在 flex wrap container 中输出允许多选的 motion.button wrapper tags。
Pill active traits classes:bg-[#1C2E1E] text-white shadow-md shadow-emerald-950/5 transform。显示一个使用 lucide-react 的 check icon,并通过 type: "spring", stiffness: 300, damping: 20 以 drop-in 方式出现。
Pill inactive traits classes:bg-white text-[#1C2E1E] border border-[#F1F3F1] hover:bg-[#F1F3F1]/55。
Contingent Feedback Status Banner:在 service pills 下方编写一个 <AnimatePresence mode="wait">,用于跟踪 user state array length:
Empty:显示通用 placeholder“Please click to select services above.”,透明度为百分之五十(opacity: 0.5, italic, text-xs)。
Active Selection:平滑切换为一个 <motion.div> container,通过 spring 让 height 优雅变化(height: "auto")。在其中显示 acknowledgment banner,内容为“Ready to inquire about: [array.join(", ")]”,并组合一个 arrow call-to-action button“Let's Go”(text-[#4D6D47] uppercase text-xs)。使用 bg-[#FAFBF9] border rounded-2xl 设置 banner 样式。原文提示词(English)
Build a modern, interactive hero section using React, Tailwind CSS, and Framer Motion (motion/react). Ensure you follow these precise architecture and styling instructions:
1. Fonts & Global Animations
Import the Inter font from Google Fonts.
In your CSS setup, configure Tailwind to use it by default (--font-sans: 'Inter', ...).
Create a keyframe animation in CSS named blink for the typewriter cursor:
code
CSS
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.animate-blink { animation: blink 1s step-end infinite; }
2. General Page Structure
Wrap the entire application in a container div with the following classes: relative bg-white text-neutral-900 font-sans selection:bg-[#EAECE9] selection:text-[#1C2E1E] antialiased overflow-x-hidden flex flex-col lg:block lg:min-h-screen.
3. Background Video Component (with Native Scrubbing)
Container element: Add a div containing the background video with classes: order-last lg:order-none relative lg:absolute lg:inset-0 lg:z-0 overflow-hidden pointer-events-none w-full aspect-square md:aspect-video lg:aspect-auto lg:h-full bg-neutral-50 lg:bg-transparent.
Video element: Use <video> with muted, playsInline, preload="auto".
Video Source URL: https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260601_110537_3a579fa0-7bbc-4d94-9d25-0e816c7840f5.mp4
Classes: w-full h-full object-cover object-right lg:object-right-bottom.
Scrubbing/Playback Logic via useEffect hooks:
Desktop Mouse Scrubbing Hook: Listen to the window mousemove event. If window.innerWidth < 1024, ignore (disable scrubbing). Store the mouse 'previous X' coordinate to calculate the delta against 'current X'. Update the target scrub time based on (delta / window.innerWidth) * 0.8 * video.duration. Clamp the time between 0 and duration. Set video.currentTime = targetTime. Bind a seeked event listener to ensure smooth tracking frame to frame.
Mobile Autoplay Hook: Because scrubbing is disabled on mobile frames, trigger normal playback for screens < 1024 width: video.autoplay = true and video.play().
4. Interactive Navbar
Header wrapper: Wrap the Navbar in <header className="fixed top-0 inset-x-0 z-10 px-5 sm:px-8 py-4 sm:py-5 flex flex-row justify-between items-center bg-transparent">
Logo (Left side): Flex row with gap-3.
Text: Mainframe® (using the ® symbol). Classes: text-[21px] sm:text-[26px] tracking-tight text-black font-medium select-none.
Icon block right beside it: An asterisk ✱. Classes: text-[25px] sm:text-[30px] text-black select-none tracking-[-0.02em] font-medium leading-none mb-1.
Desktop Nav Links (Center): Flex row, hidden md:flex, text-[23px] text-black. Links are "Labs", "Studio", "Openings", "Shop" separated by <span className="opacity-40">, </span> dividers. Hover states should use hover:opacity-60 transition-opacity.
Desktop CTA (Right): Hidden on mobile. A link reading "Get in touch" mapped with text-[23px] text-black underline underline-offset-2 hover:opacity-60 transition-opacity.
Mobile Menu Logic:
Hamburger <button> visible below md. Has three w-6 h-[2px] bg-black spans.
Hook it to a local state isMobileMenuOpen. When open, animate the burger into an 'X' (top bar rotate-45 translate-y-[7px], middle bar opacity-0, bottom bar -rotate-45 -translate-y-[7px]). All spans need transition-all duration-300.
Create a full screen Mobile Navigation Overlay div hidden on Desktop. Fixed inset-0 z-[9] with bg-white/95 backdrop-blur-sm. Apply opacity-100 pointer-events-auto when isMobileMenuOpen is true; otherwise, opacity-0 pointer-events-none.
5. Content Layout Container
Below the background video and relative to it, add a content grouping layer: <div className="relative z-10 flex flex-col order-first lg:order-none w-full bg-white lg:bg-transparent pb-8 lg:pb-0 lg:min-h-screen">
Inside that, the overarching layout engine: <main id="spade-hero" className="w-full max-w-7xl mx-auto px-6 py-12 flex-1 flex flex-col justify-center">
6. Typewriter Hook and Headline
Implement a custom useTypewriter(text, speed = 38, startDelay = 600) React hook. It uses setTimeout and setInterval to iteratively build a string slice by slice. It must return an object: { displayed: string, done: boolean }.
Run the hook with the string "we'd love tonhear from you!".
Wrap the headline securely in a motion.div configured to drop-in (initial: opacity: 0, y: 20, animate: opacity: 1, y: 0, transition duration 0.6).
Render your hook text inside <h1 className="text-5xl md:text-6xl lg:text-[76px] font-normal tracking-tight text-black leading-[1.08] mb-8 select-none w-full whitespace-pre-wrap">.
While typing (!done), output a <span className="inline-block w-[2px] h-[1.1em] bg-black align-middle ml-[2px] animate-blink" /> cursor at the end of the displayed text string.
7. Secondary Description Text
Another motion.div (delay 0.1s from the headline).
Content: <p> tag that reads: Whether you have questions, feedback, <br /> drop us a message and we'll get back to you as soon as possible.
Classes: text-lg md:text-xl text-[#5A635A] leading-relaxed font-normal mb-14 max-w-2xl.
8. Interactive Multi-Select Service Pills
Using setServices track an array ["Brand", "Digital", "Campaign", "Other"].
The prompt Title: "What sort of service?" (text-2xl font-medium tracking-tight mb-2). Subtitle: "Select all that apply" (opacity-85 text-[#738273] mb-8).
Iterate over the options natively outputting motion.button wrapper tags allowing multiple selections inside a flex wrap container.
Pill active traits classes: bg-[#1C2E1E] text-white shadow-md shadow-emerald-950/5 transform. Show a check icon (lucide-react) dropping in using type: "spring", stiffness: 300, damping: 20.
Pill inactive traits classes: bg-white text-[#1C2E1E] border border-[#F1F3F1] hover:bg-[#F1F3F1]/55.
Contingent Feedback Status Banner: Underneath your service pills, write an <AnimatePresence mode="wait"> that tracks user state array length:
Empty: Show a generic placeholder indicating "Please click to select services above." at fifty percent opacity (opacity: 0.5, italic, text-xs).
Active Selection: Swap cleanly into a container <motion.div> that springs height gracefully (height: "auto"). Inside, display an acknowledgment banner reading "Ready to inquire about: [array.join(", ")]" combined with an arrow call-to-action button "Let's Go" (text-[#4D6D47] uppercase text-xs). Style the banner with bg-[#FAFBF9] border rounded-2xl.