提示词

全部提示词

UI/UX 提示词: Full-screen dark hero for security company "Sentinel AI" with an embedded Spline 3D scene background

Anthropic 的UI/UX 提示词条目,保存在 Eldergenix 提示词库中。为保留来源和证据价值,原始正文保持未翻译。

UI/UX 提示词AnthropicReact, TypeScript, Vite, Tailwind CSS, shadcn/ui, Spline
代码库
示例输出
演示
提示词
Anthropic
# Sentinel AI — Cinematic Security Hero Section

## Overview

Build a full-screen, dark hero landing page for a security company called "Sentinel AI". The page floats a fixed transparent navbar and a bottom-left-anchored content block over an embedded Spline 3D scene that fills the viewport. Clicks pass through the content area to the interactive 3D scene, except for the buttons, which re-enable pointer events. There is no light mode and no mobile hamburger menu — the nav links and CTA simply hide below the `md` breakpoint.

## Tech Stack

- **Framework:** React with Vite, TypeScript.
- **Styling:** Tailwind CSS with the `tailwindcss-animate` plugin.
- **UI primitives:** shadcn/ui `Button` component built with `class-variance-authority` for the button variants.
- **3D embed:** `@splinetool/react-spline` and `@splinetool/runtime` for the embedded 3D Spline scene used as the background.
- **Font:** Google Fonts "Sora" (weights 300, 400, 500, 600, 700).
- **Notable techniques:** lazily-loaded Spline scene via `React.lazy` + `Suspense`, fluid `clamp()` typography, staggered fade-up entrance animations, `pointer-events-none` passthrough with `pointer-events-auto` button re-enable, dark-only HSL CSS custom properties mapped into Tailwind.

## Global Setup

### `index.html`

Load the Sora font in the `<head>`:

```html
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  href="https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700&display=swap"
  rel="stylesheet"
/>
```

## Color Palette

All colors are HSL CSS custom properties, dark only — there is no light mode. Define them on `:root` in `src/index.css` and map them in Tailwind config using the `hsl(var(--variable))` pattern.

| Token | Value | Notes |
| --- | --- | --- |
| `--background` | `0 0% 10%` | Dark charcoal |
| `--foreground` | `0 0% 96%` | Near-white |
| `--primary` | `119 99% 46%` | Vivid green |
| `--primary-foreground` | `0 0% 4%` | Near-black |
| `--secondary` | `0 0% 18%` | |
| `--secondary-foreground` | `0 0% 96%` | |
| `--muted` | `0 0% 16%` | |
| `--muted-foreground` | `0 0% 60%` | |
| `--accent` | `119 99% 46%` | Same vivid green as primary |
| `--accent-foreground` | `0 0% 4%` | |
| `--destructive` | `0 84% 60%` | |
| `--border` | `0 0% 20%` | |
| `--input` | `0 0% 20%` | |
| `--ring` | `119 99% 46%` | |
| `--radius` | `0.5rem` | |
| `--nav-button` | `0 0% 18%` | Custom token |
| `--hero-bg` | `0 0% 8%` | The darkest background, nearly black |

Add two custom Tailwind color tokens: `nav-button` and `hero-bg`.

### `src/index.css`

```css
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 10%;
    --foreground: 0 0% 96%;

    --primary: 119 99% 46%;
    --primary-foreground: 0 0% 4%;

    --secondary: 0 0% 18%;
    --secondary-foreground: 0 0% 96%;

    --muted: 0 0% 16%;
    --muted-foreground: 0 0% 60%;

    --accent: 119 99% 46%;
    --accent-foreground: 0 0% 4%;

    --destructive: 0 84% 60%;

    --border: 0 0% 20%;
    --input: 0 0% 20%;
    --ring: 119 99% 46%;

    --radius: 0.5rem;

    --nav-button: 0 0% 18%;
    --hero-bg: 0 0% 8%;
  }

  * {
    @apply border-border;
  }

  body {
    @apply bg-background text-foreground font-sora antialiased;
  }

  ::selection {
    @apply bg-primary text-primary-foreground;
  }
}
```

## Tailwind Config — `tailwind.config.ts`

- `darkMode: ["class"]`, `content: ["./index.html", "./src/**/*.{ts,tsx}"]`.
- `fontFamily.sora: ["Sora", "sans-serif"]` — the body uses `font-sora antialiased`.
- Map every color token above via `hsl(var(--…))`, including the `primary`, `secondary`, `muted`, and `accent` color groups with their `DEFAULT` + `foreground` members, plus the standalone `border`, `input`, `ring`, `background`, `foreground`, `destructive`, `nav-button`, and `hero-bg` tokens.
- `borderRadius`: `lg: var(--radius)`, `md: calc(var(--radius) - 2px)`, `sm: calc(var(--radius) - 4px)`.
- Plugin: `tailwindcss-animate`.

```ts
import type { Config } from "tailwindcss";
import animate from "tailwindcss-animate";

export default {
  darkMode: ["class"],
  content: ["./index.html", "./src/**/*.{ts,tsx}"],
  theme: {
    extend: {
      fontFamily: {
        sora: ["Sora", "sans-serif"],
      },
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        secondary: {
          DEFAULT: "hsl(var(--secondary))",
          foreground: "hsl(var(--secondary-foreground))",
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))",
        },
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))",
        },
        destructive: {
          DEFAULT: "hsl(var(--destructive))",
        },
        "nav-button": "hsl(var(--nav-button))",
        "hero-bg": "hsl(var(--hero-bg))",
      },
      borderRadius: {
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
        sm: "calc(var(--radius) - 4px)",
      },
      keyframes: {
        "fade-up": {
          "0%": {
            opacity: "0",
            transform: "translateY(20px)",
            filter: "blur(4px)",
          },
          "100%": {
            opacity: "1",
            transform: "translateY(0)",
            filter: "blur(0)",
          },
        },
        "fade-in": {
          "0%": { opacity: "0" },
          "100%": { opacity: "1" },
        },
      },
      animation: {
        "fade-up": "fade-up 0.7s cubic-bezier(0.16, 1, 0.3, 1) forwards",
        "fade-in": "fade-in 0.5s ease-out forwards",
      },
    },
  },
  plugins: [animate],
} satisfies Config;
```

## Animations

Two custom keyframes/animations defined in Tailwind config:

- **`fade-up`** — `0%`: `opacity: 0`, `transform: translateY(20px)`, `filter: blur(4px)`; `100%`: `opacity: 1`, `transform: translateY(0)`, `filter: blur(0)`. Animation: `fade-up 0.7s cubic-bezier(0.16, 1, 0.3, 1) forwards`.
- **`fade-in`** — `0%`: `opacity: 0`; `100%`: `opacity: 1`. Animation: `fade-in 0.5s ease-out forwards`.

Animated content elements start at `opacity-0` with the `animate-fade-up` class, and stagger their entrance with an inline `style={{ animationDelay: "Xs" }}`.

## Layout & Page Wrapper

### App shell

`src/App.tsx` simply renders the page:

```tsx
import Index from "@/pages/Index";

const App = () => <Index />;

export default App;
```

### Page — `src/pages/Index.tsx`

A simple wrapper containing `<Navbar />` and `<HeroSection />`:

```tsx
import HeroSection from "@/components/HeroSection";
import Navbar from "@/components/Navbar";

const Index = () => (
  <div className="bg-hero-bg min-h-screen">
    <Navbar />
    <HeroSection />
  </div>
);

export default Index;
```

## Navbar

Fixed, transparent, floating over the Spline scene.

- **Container:** `<header>` with `fixed top-0 left-0 right-0 z-50 flex items-center justify-between px-8 lg:px-16 py-5`.
- **Left — logo:** `<a href="#">` text `SENTINEL`, classes `text-foreground text-xl font-semibold tracking-tight`.
- **Center — nav links:** a `<nav>` with `hidden md:flex items-center gap-8` (hidden on mobile). Render from the array `["Services", "About Us", "Projects", "Team", "Contacts"]`. Each link is an `<a>` with classes `text-sm text-muted-foreground hover:text-foreground transition-colors uppercase tracking-widest`, keyed by its label, with `href` derived from the label via a `toAnchor` helper that lowercases and replaces whitespace with hyphens (e.g. `About Us` → `#about-us`).
- **Right — CTA:** shadcn `Button` with `variant="navCta"` and `size="lg"`, text `Get Quote`, plus classes `hidden md:inline-flex rounded-lg uppercase text-xs tracking-widest px-6`.

```tsx
import { Button } from "@/components/ui/button";

const NAV_LINKS = ["Services", "About Us", "Projects", "Team", "Contacts"];

const toAnchor = (label: string) =>
  `#${label.toLowerCase().replace(/\s+/g, "-")}`;

const Navbar = () => (
  <header className="fixed top-0 left-0 right-0 z-50 flex items-center justify-between px-8 lg:px-16 py-5">
    <a
      href="#"
      className="text-foreground text-xl font-semibold tracking-tight"
    >
      SENTINEL
    </a>

    <nav className="hidden md:flex items-center gap-8">
      {NAV_LINKS.map((link) => (
        <a
          key={link}
          href={toAnchor(link)}
          className="text-sm text-muted-foreground hover:text-foreground transition-colors uppercase tracking-widest"
        >
          {link}
        </a>
      ))}
    </nav>

    <Button
      variant="navCta"
      size="lg"
      className="hidden md:inline-flex rounded-lg uppercase text-xs tracking-widest px-6"
    >
      Get Quote
    </Button>
  </header>
);

export default Navbar;
```

## Hero Section

Full-screen section with content anchored to the bottom-left.

### Structure

- **Outer `<section>`:** `relative min-h-screen flex items-end bg-hero-bg overflow-hidden`.
- **Spline 3D background (absolute, full-size):** lazily loaded via `lazy(() => import("@splinetool/react-spline"))`, wrapped in `<Suspense>` with the fallback `<div className="absolute inset-0 bg-hero-bg" />`. The `<Spline>` component receives the scene via a `SPLINE_SCENE` constant and `className="w-full h-full"`, all placed inside `<div className="absolute inset-0">`.
- **Dark overlay** (for content legibility): `<div className="absolute inset-0 bg-black/30 z-[1] pointer-events-none" />`.
- **Content container:** `relative z-10 pointer-events-none w-full max-w-[90%] sm:max-w-md lg:max-w-2xl px-6 md:px-10 pb-10 md:pb-10 pt-32`. The whole container is `pointer-events-none` so clicks pass through to the interactive Spline scene; only the buttons re-enable clicks with `pointer-events-auto`.

### Content elements (staggered `animate-fade-up`, each starting `opacity-0`)

1. **Heading** (`animationDelay: "0.2s"`): an `<h1>` reading `SENTINEL ` in white plus ` AI` in primary green. The `AI` part is wrapped in `<span className="text-primary">`. Classes: `opacity-0 animate-fade-up text-[clamp(3rem,8vw,6rem)] font-bold leading-[1.05] tracking-[-0.05em] text-foreground mb-2 md:mb-4 uppercase`.
2. **Subheading** (`animationDelay: "0.4s"`): a `<p>` reading `We implement security correctly.` Classes: `opacity-0 animate-fade-up text-foreground/80 text-[clamp(1.125rem,2.5vw,1.875rem)] font-light mb-3 md:mb-6`.
3. **Description** (`animationDelay: "0.55s"`): a `<p>` reading `Enterprise security systems built in days. AI-powered surveillance deployed with zero-trust architecture. Smart access control set up for your entire facility. All of it done right, not just fast.` Classes: `opacity-0 animate-fade-up text-muted-foreground text-[clamp(0.875rem,1.5vw,1.25rem)] font-light mb-4 md:mb-8`.
4. **Two CTA buttons** (`animationDelay: "0.7s"`): wrapped in `<div className="opacity-0 animate-fade-up flex flex-wrap gap-3 font-bold">`. Both are plain `<button type="button">` elements (not the shadcn `Button`) with `pointer-events-auto` to re-enable clicks:
   - **`Book a Call`** — `pointer-events-auto bg-primary text-primary-foreground px-6 py-3 md:px-8 md:py-4 text-sm rounded-sm cursor-pointer hover:brightness-110 transition-all active:scale-[0.97]`.
   - **`Our Work`** — `pointer-events-auto bg-white text-background px-6 py-3 md:px-8 md:py-4 text-sm rounded-sm cursor-pointer hover:brightness-90 transition-all active:scale-[0.97]`.
5. **Trust line** (`animationDelay: "0.85s"`): a `<p>` reading `Trusted security partner. Columbus, OH. 12 systems deployed.` Classes: `opacity-0 animate-fade-up text-muted-foreground/60 text-xs font-light mt-4 md:mt-6`.

### `src/components/HeroSection.tsx`

```tsx
import { lazy, Suspense } from "react";

const Spline = lazy(() => import("@splinetool/react-spline"));

const SPLINE_SCENE = "/spline/scene.splinecode";

const HeroSection = () => (
  <section className="relative min-h-screen flex items-end bg-hero-bg overflow-hidden">
    {/* Spline 3D background */}
    <div className="absolute inset-0">
      <Suspense fallback={<div className="absolute inset-0 bg-hero-bg" />}>
        <Spline scene={SPLINE_SCENE} className="w-full h-full" />
      </Suspense>
    </div>

    {/* Dark overlay for content legibility */}
    <div className="absolute inset-0 bg-black/30 z-[1] pointer-events-none" />

    {/* Content — anchored bottom-left, clicks pass through to the scene */}
    <div className="relative z-10 pointer-events-none w-full max-w-[90%] sm:max-w-md lg:max-w-2xl px-6 md:px-10 pb-10 md:pb-10 pt-32">
      <h1
        className="opacity-0 animate-fade-up text-[clamp(3rem,8vw,6rem)] font-bold leading-[1.05] tracking-[-0.05em] text-foreground mb-2 md:mb-4 uppercase"
        style={{ animationDelay: "0.2s" }}
      >
        SENTINEL <span className="text-primary">AI</span>
      </h1>

      <p
        className="opacity-0 animate-fade-up text-foreground/80 text-[clamp(1.125rem,2.5vw,1.875rem)] font-light mb-3 md:mb-6"
        style={{ animationDelay: "0.4s" }}
      >
        We implement security correctly.
      </p>

      <p
        className="opacity-0 animate-fade-up text-muted-foreground text-[clamp(0.875rem,1.5vw,1.25rem)] font-light mb-4 md:mb-8"
        style={{ animationDelay: "0.55s" }}
      >
        Enterprise security systems built in days. AI-powered surveillance
        deployed with zero-trust architecture. Smart access control set up for
        your entire facility. All of it done right, not just fast.
      </p>

      <div
        className="opacity-0 animate-fade-up flex flex-wrap gap-3 font-bold"
        style={{ animationDelay: "0.7s" }}
      >
        <button
          type="button"
          className="pointer-events-auto bg-primary text-primary-foreground px-6 py-3 md:px-8 md:py-4 text-sm rounded-sm cursor-pointer hover:brightness-110 transition-all active:scale-[0.97]"
        >
          Book a Call
        </button>
        <button
          type="button"
          className="pointer-events-auto bg-white text-background px-6 py-3 md:px-8 md:py-4 text-sm rounded-sm cursor-pointer hover:brightness-90 transition-all active:scale-[0.97]"
        >
          Our Work
        </button>
      </div>

      <p
        className="opacity-0 animate-fade-up text-muted-foreground/60 text-xs font-light mt-4 md:mt-6"
        style={{ animationDelay: "0.85s" }}
      >
        Trusted security partner. Columbus, OH. 12 systems deployed.
      </p>
    </div>
  </section>
);

export default HeroSection;
```

### Spline scene asset

The `<Spline>` `scene` prop points to a locally vendored asset: `/spline/scene.splinecode` (served from `public/spline/scene.splinecode`). The original prompt specifies the exact remote Spline scene URL `https://prod.spline.design/SLK6B8KZ3LRLKIYK/scene.splinecode` — if rebuilding from scratch you can use the remote URL directly, or vendor it locally as done here.

## shadcn/ui Button — `src/components/ui/button.tsx`

The shadcn `Button` is built with `class-variance-authority`. In addition to the standard variants it defines three custom variants — `navCta`, `hero`, and `heroOutline`:

- **`navCta`** — `text-foreground bg-nav-button hover:bg-nav-button/80 active:scale-[0.97] transition-all` (used by the navbar CTA).
- **`hero`** — `bg-primary text-primary-foreground rounded-sm font-bold hover:brightness-110 active:scale-[0.97] transition-all`.
- **`heroOutline`** — `bg-white text-background rounded-sm font-bold hover:brightness-90 active:scale-[0.97] transition-all`.

```tsx
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";

import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-foreground hover:bg-destructive/90",
        outline:
          "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
        secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
        navCta:
          "text-foreground bg-nav-button hover:bg-nav-button/80 active:scale-[0.97] transition-all",
        hero: "bg-primary text-primary-foreground rounded-sm font-bold hover:brightness-110 active:scale-[0.97] transition-all",
        heroOutline:
          "bg-white text-background rounded-sm font-bold hover:brightness-90 active:scale-[0.97] transition-all",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-11 rounded-md px-8",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  },
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button";
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    );
  },
);
Button.displayName = "Button";

export { Button, buttonVariants };
```

## Implementation Notes

- The entire content area uses `pointer-events-none` so clicks pass through to the Spline scene; the two hero buttons re-enable interaction with `pointer-events-auto`.
- Responsive fluid typography uses `clamp()` for the heading, subheading, and description.
- The hero content is anchored to the bottom-left of the viewport via `flex items-end` on the section combined with padding-bottom on the content container.
- No hamburger menu on mobile — the nav links and CTA simply hide (`hidden md:flex` / `hidden md:inline-flex`).
- Dark only — there is no light mode.

## File Structure

```
sentinel-ai-hero/
├── index.html
├── package.json
├── postcss.config.js
├── tailwind.config.ts
├── tsconfig.json
├── vite.config.ts
├── public/
│   └── spline/
│       └── scene.splinecode
└── src/
    ├── App.tsx
    ├── main.tsx
    ├── index.css
    ├── components/
    │   ├── HeroSection.tsx
    │   ├── Navbar.tsx
    │   └── ui/
    │       └── button.tsx
    ├── lib/
    │   └── utils.ts
    └── pages/
        └── Index.tsx
```
Stefan Creadore · @Eldergenix - 来自 Eldergenix 源代码库的提示词库