提示词
---
**重现“分类”区块的提示词:**
> 使用 React + Tailwind CSS 构建一个“分类”区块,严格遵循以下规格:
>
> **区块容器:**
> - 全宽 `<section>`,使用 `bg-white`、`text-white`、`min-h-screen`。
> - Flexbox 纵向布局,使用 `justify-center` 让网格内容垂直居中。
> - 区块本身不设置水平或垂直内边距。
>
> **网格布局:**
> - CSS 网格:移动端使用 `grid-cols-1`,中等及以上屏幕使用 `md:grid-cols-3`。
> - 整个网格使用基于 IntersectionObserver 的显现动画(threshold `0.1`):在 `duration-1000 ease-out` 内从 `opacity-0 translate-y-12` 过渡到 `opacity-100 translate-y-0`。
>
> **3 张分类卡片及其准确数据:**
> 1. 名称:`"face"` | 视频:`https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260518_203023_87a26602-2898-4acc-a396-c7a2b5ad84fd.mp4`
> 2. 名称:`"beauty tools"` | 视频:`https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260518_203415_b86e3f19-2aec-46cd-9a86-b64c40118e38.mp4`
> 3. 名称:`"body"` | 视频:`https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260518_203051_85fee398-ea01-4aa0-972b-137a74213be5.mp4`
>
> **卡片布局(每张卡片):**
> - `position: relative`,采用 Flexbox 纵向布局,并使用 `justify-between`、`items-start`。
> - 内边距:移动端为 `p-6`,`sm:p-8`,`md:p-12`。
> - 高度:移动端为 `min-h-[400px]`,`sm:min-h-[500px]`,`md:min-h-[750px]`。
> - 使用 `overflow-hidden`。
> - 每张卡片使用交错的 `transitionDelay`,值为 `index * 150ms`。
> - 使用 `group` 实现悬停交互。
>
> **背景视频:**
> - `<video>` 元素带有以下属性:`autoPlay`、`loop`、`muted`、`playsInline`。
> - 绝对定位:`absolute inset-0 w-full h-full object-cover`。
> - 悬停效果:通过 `transition-transform group-hover:scale-105`,在 `duration-700` 内缩放至 `scale-105`。
> - `src` 属性直接设置为视频 URL(不使用 `<source>` 标签)。
>
> **深色遮罩:**
> - 一个绝对定位在视频上方的 `<div>`:`absolute inset-0`。
> - 默认:`bg-black/10`。悬停时:`group-hover:bg-black/20`。
> - 使用 `transition-colors duration-500`。
>
> **分类名称(竖排文字):**
> - `<h2>` 标签,位于遮罩上方:`relative z-10`。
> - 字号:移动端 `text-5xl`,`sm:text-6xl`,`md:text-7xl`,`lg:text-8xl`。字重:`font-medium`。
> - **竖排文字**:通过内联样式 `writingMode: 'vertical-lr'` 配合 `transform: 'rotate(180deg)'` 实现(使文字从下向上阅读)。
> - 悬停时:在 `duration-500` 内应用 `group-hover:-translate-y-2`。
> - 文本为小写(按数据原样渲染:"face"、"beauty tools"、"body")。
>
> **购买按钮:**
> - `<button>` 使用 `btn-primary` 类(见下方 CSS),并加上 `relative z-10 mt-auto px-8 py-3 bg-white text-black rounded-full text-sm`。
> - 文本:小写的 `"shop {category name}"`(例如 "shop face"、"shop beauty tools"、"shop body")。
> - `mt-auto` 将按钮推至卡片底部。
>
> **`btn-primary` 所需的 CSS(位于全局样式表中):**
> ```css
> .btn-primary {
> position: relative;
> overflow: hidden;
> transition: transform 0.3s ease, box-shadow 0.3s ease;
> }
> .btn-primary::before {
> content: '';
> position: absolute;
> inset: 0;
> background: linear-gradient(120deg, transparent 0%, rgba(0, 0, 0, 0.05) 50%, transparent 100%);
> transform: translateX(-100%);
> transition: transform 0.5s ease;
> }
> .btn-primary:hover {
> transform: translateY(-2px);
> box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
> }
> .btn-primary:hover::before {
> transform: translateX(100%);
> }
> .btn-primary:active {
> transform: translateY(0);
> box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
> }
> ```
>
> **IntersectionObserver hook(`useInView`):**
> - 接受一个 `threshold` 参数(默认值为 `0.15`),并使用 ref。
> - 观察该元素;一旦 `isIntersecting` 为 true,就设置 `isVisible = true` 并停止观察(单次动画)。
> - 返回 `{ ref, isVisible }`。
> - 此区块调用时使用 threshold `0.1`。
---原文提示词(English)
---
**Prompt to recreate the "Categories" section:**
> Build a "Categories" section in React + Tailwind CSS with the following exact specifications:
>
> **Section Container:**
> - Full-width `<section>` with `bg-white`, `text-white`, `min-h-screen`.
> - Flexbox column layout with `justify-center` to vertically center the grid content.
> - No horizontal or vertical padding on the section itself.
>
> **Grid Layout:**
> - A CSS grid: `grid-cols-1` on mobile, `md:grid-cols-3` on medium+.
> - The entire grid uses an IntersectionObserver-based reveal animation (threshold `0.1`): transitions from `opacity-0 translate-y-12` to `opacity-100 translate-y-0` over `duration-1000 ease-out`.
>
> **3 Category Cards with exact data:**
> 1. Name: `"face"` | Video: `https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260518_203023_87a26602-2898-4acc-a396-c7a2b5ad84fd.mp4`
> 2. Name: `"beauty tools"` | Video: `https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260518_203415_b86e3f19-2aec-46cd-9a86-b64c40118e38.mp4`
> 3. Name: `"body"` | Video: `https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260518_203051_85fee398-ea01-4aa0-972b-137a74213be5.mp4`
>
> **Card Layout (each card):**
> - `position: relative`, flexbox column with `justify-between`, `items-start`.
> - Padding: `p-6` mobile, `sm:p-8`, `md:p-12`.
> - Heights: `min-h-[400px]` mobile, `sm:min-h-[500px]`, `md:min-h-[750px]`.
> - `overflow-hidden`.
> - Each card has a staggered `transitionDelay` of `index * 150ms`.
> - Uses `group` for hover interactions.
>
> **Background Video:**
> - `<video>` element with attributes: `autoPlay`, `loop`, `muted`, `playsInline`.
> - Positioned absolutely: `absolute inset-0 w-full h-full object-cover`.
> - Hover effect: `scale-105` over `duration-700` via `transition-transform group-hover:scale-105`.
> - The `src` attribute is set directly from the video URL (no `<source>` tag).
>
> **Dark Overlay:**
> - A `<div>` absolutely positioned over the video: `absolute inset-0`.
> - Default: `bg-black/10`. On hover: `group-hover:bg-black/20`.
> - `transition-colors duration-500`.
>
> **Category Name (vertical text):**
> - `<h2>` tag, positioned above overlay: `relative z-10`.
> - Font sizes: `text-5xl` mobile, `sm:text-6xl`, `md:text-7xl`, `lg:text-8xl`. Weight: `font-medium`.
> - **Vertical text**: achieved with inline style `writingMode: 'vertical-lr'` combined with `transform: 'rotate(180deg)'` (this makes text read bottom-to-top).
> - Hover: `group-hover:-translate-y-2` over `duration-500`.
> - Text is lowercase (rendered as-is from the data: "face", "beauty tools", "body").
>
> **Shop Button:**
> - `<button>` with class `btn-primary` (see CSS below) plus `relative z-10 mt-auto px-8 py-3 bg-white text-black rounded-full text-sm`.
> - Text: `"shop {category name}"` in lowercase (e.g., "shop face", "shop beauty tools", "shop body").
> - `mt-auto` pushes the button to the bottom of the card.
>
> **Required CSS for `btn-primary` (in global stylesheet):**
> ```css
> .btn-primary {
> position: relative;
> overflow: hidden;
> transition: transform 0.3s ease, box-shadow 0.3s ease;
> }
> .btn-primary::before {
> content: '';
> position: absolute;
> inset: 0;
> background: linear-gradient(120deg, transparent 0%, rgba(0, 0, 0, 0.05) 50%, transparent 100%);
> transform: translateX(-100%);
> transition: transform 0.5s ease;
> }
> .btn-primary:hover {
> transform: translateY(-2px);
> box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
> }
> .btn-primary:hover::before {
> transform: translateX(100%);
> }
> .btn-primary:active {
> transform: translateY(0);
> box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
> }
> ```
>
> **IntersectionObserver hook (`useInView`):**
> - Accepts a `threshold` parameter (default `0.15`), uses a ref.
> - Observes the element; once `isIntersecting` is true, sets `isVisible = true` and unobserves (one-shot animation).
> - Returns `{ ref, isVisible }`.
> - This section calls it with threshold `0.1`.
---