Spaces:
Running
Running
Reupload OmniDev clean version
Browse files
components/editor/ask-ai/index.tsx
CHANGED
|
@@ -16,6 +16,7 @@ import { ReImagine } from "@/components/editor/ask-ai/re-imagine";
|
|
| 16 |
import { Selector } from "@/components/editor/ask-ai/selector";
|
| 17 |
import { PromptBuilder } from "@/components/editor/ask-ai/prompt-builder";
|
| 18 |
import { PromptEnhancer } from "@/components/editor/ask-ai/prompt-enhancer";
|
|
|
|
| 19 |
import { useUser } from "@/hooks/useUser";
|
| 20 |
import { useLoginModal } from "@/components/contexts/login-context";
|
| 21 |
import { Settings } from "./settings";
|
|
@@ -290,6 +291,7 @@ export const AskAi = ({
|
|
| 290 |
setEnhancedSettings={setEnhancedSettings}
|
| 291 |
/>
|
| 292 |
<PromptEnhancer prompt={prompt} setPrompt={setPrompt} enhancedSettings={enhancedSettings!} />
|
|
|
|
| 293 |
<Settings
|
| 294 |
open={openProvider}
|
| 295 |
error={providerError}
|
|
|
|
| 16 |
import { Selector } from "@/components/editor/ask-ai/selector";
|
| 17 |
import { PromptBuilder } from "@/components/editor/ask-ai/prompt-builder";
|
| 18 |
import { PromptEnhancer } from "@/components/editor/ask-ai/prompt-enhancer";
|
| 19 |
+
import { HeroPresets } from "@/components/editor/hero-presets";
|
| 20 |
import { useUser } from "@/hooks/useUser";
|
| 21 |
import { useLoginModal } from "@/components/contexts/login-context";
|
| 22 |
import { Settings } from "./settings";
|
|
|
|
| 291 |
setEnhancedSettings={setEnhancedSettings}
|
| 292 |
/>
|
| 293 |
<PromptEnhancer prompt={prompt} setPrompt={setPrompt} enhancedSettings={enhancedSettings!} />
|
| 294 |
+
<HeroPresets />
|
| 295 |
<Settings
|
| 296 |
open={openProvider}
|
| 297 |
error={providerError}
|
components/editor/hero-presets/index.tsx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client";
|
| 2 |
+
import { useState } from "react";
|
| 3 |
+
import { Button } from "@/components/ui/button";
|
| 4 |
+
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
| 5 |
+
import { HERO_TEMPLATES } from "@/lib/hero-templates";
|
| 6 |
+
import { useEditor } from "@/hooks/useEditor";
|
| 7 |
+
|
| 8 |
+
function insertIntoBody(html: string, snippet: string): string {
|
| 9 |
+
if (!html) return snippet;
|
| 10 |
+
const bodyOpen = html.toLowerCase().indexOf("<body");
|
| 11 |
+
if (bodyOpen === -1) return snippet + "\n" + html;
|
| 12 |
+
const afterBodyTag = html.indexOf(">", bodyOpen);
|
| 13 |
+
if (afterBodyTag === -1) return snippet + "\n" + html;
|
| 14 |
+
const head = html.slice(0, afterBodyTag + 1);
|
| 15 |
+
const tail = html.slice(afterBodyTag + 1);
|
| 16 |
+
return head + "\n" + snippet + "\n" + tail;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
export function HeroPresets() {
|
| 20 |
+
const { currentPageData, setPages } = useEditor();
|
| 21 |
+
const [open, setOpen] = useState(false);
|
| 22 |
+
const [selected, setSelected] = useState<string | null>(null);
|
| 23 |
+
|
| 24 |
+
const apply = () => {
|
| 25 |
+
if (!selected) return;
|
| 26 |
+
const t = HERO_TEMPLATES.find(x => x.id === selected);
|
| 27 |
+
if (!t) return;
|
| 28 |
+
const newHtml = insertIntoBody(currentPageData.html, t.html);
|
| 29 |
+
setPages(prev => prev.map(p => p.path === currentPageData.path ? { ...p, html: newHtml } : p));
|
| 30 |
+
setOpen(false);
|
| 31 |
+
};
|
| 32 |
+
|
| 33 |
+
return (
|
| 34 |
+
<>
|
| 35 |
+
<Button size="xs" variant="outline" className="!rounded-md" onClick={() => setOpen(true)}>Hero Presets</Button>
|
| 36 |
+
<Dialog open={open} onOpenChange={setOpen}>
|
| 37 |
+
<DialogContent className="sm:max-w-3xl !bg-neutral-900 !border-neutral-800">
|
| 38 |
+
<DialogHeader>
|
| 39 |
+
<DialogTitle className="text-neutral-100">Insert Hero Template</DialogTitle>
|
| 40 |
+
</DialogHeader>
|
| 41 |
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 max-h-[60vh] overflow-y-auto">
|
| 42 |
+
{HERO_TEMPLATES.map(t => (
|
| 43 |
+
<div key={t.id} className={`p-3 rounded-lg border cursor-pointer ${selected===t.id ? 'border-sky-500 bg-neutral-800/50' : 'border-neutral-800 bg-neutral-900/40'}`} onClick={() => setSelected(t.id)}>
|
| 44 |
+
<p className="font-medium text-neutral-200">{t.name}</p>
|
| 45 |
+
<p className="text-xs text-neutral-400">{t.id}</p>
|
| 46 |
+
<div className="mt-2 text-xs text-neutral-500 line-clamp-3">{t.html.slice(0, 200)}...</div>
|
| 47 |
+
</div>
|
| 48 |
+
))}
|
| 49 |
+
</div>
|
| 50 |
+
<DialogFooter>
|
| 51 |
+
<Button size="sm" variant="bordered" onClick={() => setOpen(false)}>Close</Button>
|
| 52 |
+
<Button size="sm" onClick={apply} disabled={!selected}>Insert</Button>
|
| 53 |
+
</DialogFooter>
|
| 54 |
+
</DialogContent>
|
| 55 |
+
</Dialog>
|
| 56 |
+
</>
|
| 57 |
+
);
|
| 58 |
+
}
|
| 59 |
+
|
lib/hero-templates.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export type HeroTemplateId =
|
| 2 |
+
| "animated-gradient"
|
| 3 |
+
| "particles"
|
| 4 |
+
| "waves"
|
| 5 |
+
| "noise"
|
| 6 |
+
| "parallax"
|
| 7 |
+
| "morphing-blobs"
|
| 8 |
+
| "rays"
|
| 9 |
+
| "grid"
|
| 10 |
+
| "shapes"
|
| 11 |
+
| "lines"
|
| 12 |
+
| "orbits";
|
| 13 |
+
|
| 14 |
+
export interface HeroTemplate {
|
| 15 |
+
id: HeroTemplateId;
|
| 16 |
+
name: string;
|
| 17 |
+
html: string; // full HTML snippet to insert inside <body>
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
export const HERO_TEMPLATES: HeroTemplate[] = [
|
| 21 |
+
{
|
| 22 |
+
id: "animated-gradient",
|
| 23 |
+
name: "Animated Gradient",
|
| 24 |
+
html: `
|
| 25 |
+
<!-- Hero: Animated Gradient -->
|
| 26 |
+
<section class="relative overflow-hidden min-h-[60vh] grid place-content-center text-center px-6 py-24">
|
| 27 |
+
<div class="absolute inset-0 bg-gradient-to-br from-sky-500/30 via-purple-500/20 to-pink-500/30 animate-[gradientMove_12s_ease_infinite] blur-3xl"></div>
|
| 28 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 29 |
+
<h1 class="text-4xl md:text-6xl font-extrabold tracking-tight text-white">Welcome to <span class="bg-clip-text text-transparent bg-gradient-to-r from-sky-400 to-purple-400">OmniDev</span></h1>
|
| 30 |
+
<p class="mt-4 text-neutral-300 text-lg">Build full-stack web projects with AI in minutes.</p>
|
| 31 |
+
<div class="mt-8 flex items-center justify-center gap-3">
|
| 32 |
+
<a href="#start" class="px-5 py-2.5 rounded-lg bg-white text-neutral-900 font-medium hover:opacity-90">Get Started</a>
|
| 33 |
+
<a href="#learn" class="px-5 py-2.5 rounded-lg bg-white/10 border border-white/20 text-white hover:bg-white/20">Learn More</a>
|
| 34 |
+
</div>
|
| 35 |
+
</div>
|
| 36 |
+
<style>
|
| 37 |
+
@keyframes gradientMove { 0%{transform:translateY(0)} 50%{transform:translateY(-20px)} 100%{transform:translateY(0)} }
|
| 38 |
+
</style>
|
| 39 |
+
</section>
|
| 40 |
+
`.trim(),
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
id: "waves",
|
| 44 |
+
name: "Waves",
|
| 45 |
+
html: `
|
| 46 |
+
<!-- Hero: Waves -->
|
| 47 |
+
<section class="relative overflow-hidden min-h-[60vh] grid place-content-center text-center px-6 py-24 bg-gradient-to-b from-neutral-900 to-neutral-950">
|
| 48 |
+
<svg class="absolute -bottom-10 left-0 w-[200%] opacity-40" viewBox="0 0 1440 320" xmlns="http://www.w3.org/2000/svg">
|
| 49 |
+
<path fill="#0ea5e9" fill-opacity="0.2" d="M0,64L48,74.7C96,85,192,107,288,122.7C384,139,480,149,576,176C672,203,768,245,864,229.3C960,213,1056,139,1152,128C1248,117,1344,171,1392,197.3L1440,224L1440,320L0,320Z" />
|
| 50 |
+
<path fill="#8b5cf6" fill-opacity="0.25" d="M0,96L60,117.3C120,139,240,181,360,192C480,203,600,181,720,160C840,139,960,117,1080,122.7C1200,128,1320,160,1380,176L1440,192L1440,320L0,320Z" />
|
| 51 |
+
</svg>
|
| 52 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 53 |
+
<h1 class="text-4xl md:text-6xl font-extrabold tracking-tight text-white">Beautiful Wave Hero</h1>
|
| 54 |
+
<p class="mt-4 text-neutral-300 text-lg">Effortless visuals with tailwind-only waves.</p>
|
| 55 |
+
</div>
|
| 56 |
+
</section>
|
| 57 |
+
`.trim(),
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
id: "noise",
|
| 61 |
+
name: "Noise Overlay",
|
| 62 |
+
html: `
|
| 63 |
+
<!-- Hero: Noise Overlay -->
|
| 64 |
+
<section class="relative overflow-hidden min-h-[60vh] grid place-content-center text-center px-6 py-24 bg-neutral-950">
|
| 65 |
+
<div class="absolute inset-0 bg-[radial-gradient(circle_at_20%_20%,rgba(14,165,233,0.2),transparent_50%),radial-gradient(circle_at_80%_30%,rgba(139,92,246,0.2),transparent_50%)]"></div>
|
| 66 |
+
<div class="absolute inset-0 opacity-[0.07]" style="background-image:url('data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'160\' height=\'160\'><filter id=\'n\'><feTurbulence baseFrequency=\'0.65\' numOctaves=\'3\' stitchTiles=\'stitch\' /></filter><rect width=\'100%\' height=\'100%\' filter=\'url(%23n)\' /></svg>');"></div>
|
| 67 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 68 |
+
<h1 class="text-4xl md:text-6xl font-extrabold text-white">Noisy Hero</h1>
|
| 69 |
+
<p class="mt-4 text-neutral-300 text-lg">Subtle noise overlay for premium feel.</p>
|
| 70 |
+
</div>
|
| 71 |
+
</section>
|
| 72 |
+
`.trim(),
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
id: "parallax",
|
| 76 |
+
name: "Parallax Layers",
|
| 77 |
+
html: `
|
| 78 |
+
<!-- Hero: Parallax Layers -->
|
| 79 |
+
<section class="relative overflow-hidden min-h-[60vh] text-center px-6 py-24 bg-neutral-950">
|
| 80 |
+
<div class="absolute inset-0 pointer-events-none">
|
| 81 |
+
<div class="absolute inset-0 bg-gradient-to-b from-sky-500/10 to-purple-500/10"></div>
|
| 82 |
+
<div class="absolute -top-10 -left-10 w-64 h-64 bg-sky-500/20 rounded-full blur-3xl translate-y-4"></div>
|
| 83 |
+
<div class="absolute -bottom-10 -right-10 w-72 h-72 bg-purple-500/20 rounded-full blur-3xl -translate-y-4"></div>
|
| 84 |
+
</div>
|
| 85 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 86 |
+
<h1 class="text-4xl md:text-6xl font-extrabold text-white">Parallax Hero</h1>
|
| 87 |
+
<p class="mt-4 text-neutral-300 text-lg">Layered glow with depth illusion.</p>
|
| 88 |
+
</div>
|
| 89 |
+
</section>
|
| 90 |
+
`.trim(),
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
id: "morphing-blobs",
|
| 94 |
+
name: "Morphing Blobs",
|
| 95 |
+
html: `
|
| 96 |
+
<!-- Hero: Morphing Blobs -->
|
| 97 |
+
<section class="relative overflow-hidden min-h-[60vh] grid place-content-center text-center px-6 py-24 bg-neutral-950">
|
| 98 |
+
<svg class="absolute inset-0 w-full h-full opacity-20" viewBox="0 0 800 400" preserveAspectRatio="none">
|
| 99 |
+
<defs>
|
| 100 |
+
<linearGradient id="g" x1="0" x2="1" y1="0" y2="1">
|
| 101 |
+
<stop offset="0%" stop-color="#0ea5e9" />
|
| 102 |
+
<stop offset="100%" stop-color="#8b5cf6" />
|
| 103 |
+
</linearGradient>
|
| 104 |
+
</defs>
|
| 105 |
+
<path fill="url(#g)">
|
| 106 |
+
<animate attributeName="d" dur="12s" repeatCount="indefinite" values="
|
| 107 |
+
M0,320L80,288C160,256,320,192,480,165.3C640,139,800,149,960,154.7L960,400L0,400Z;
|
| 108 |
+
M0,320L120,261.3C240,203,480,85,720,101.3C960,117,1200,267,1440,288L1440,400L0,400Z;
|
| 109 |
+
M0,320L80,288C160,256,320,192,480,165.3C640,139,800,149,960,154.7L960,400L0,400Z" />
|
| 110 |
+
</path>
|
| 111 |
+
</svg>
|
| 112 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 113 |
+
<h1 class="text-4xl md:text-6xl font-extrabold tracking-tight text-white">Morphing Blobs Hero</h1>
|
| 114 |
+
<p class="mt-4 text-neutral-300 text-lg">SVG animated blobs with gradient.</p>
|
| 115 |
+
</div>
|
| 116 |
+
</section>
|
| 117 |
+
`.trim(),
|
| 118 |
+
},
|
| 119 |
+
{
|
| 120 |
+
id: "rays",
|
| 121 |
+
name: "Rays / Aurora",
|
| 122 |
+
html: `
|
| 123 |
+
<!-- Hero: Rays / Aurora -->
|
| 124 |
+
<section class="relative overflow-hidden min-h-[60vh] grid place-content-center text-center px-6 py-24 bg-neutral-950">
|
| 125 |
+
<div class="absolute inset-0" style="background: conic-gradient(from 90deg at 50% 50%, rgba(14,165,233,.15), rgba(139,92,246,.15), rgba(236,72,153,.15)); filter: blur(60px);"></div>
|
| 126 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 127 |
+
<h1 class="text-4xl md:text-6xl font-extrabold text-white">Aurora Hero</h1>
|
| 128 |
+
<p class="mt-4 text-neutral-300 text-lg">Conic gradient rays for an Aurora feel.</p>
|
| 129 |
+
</div>
|
| 130 |
+
</section>
|
| 131 |
+
`.trim(),
|
| 132 |
+
},
|
| 133 |
+
{
|
| 134 |
+
id: "grid",
|
| 135 |
+
name: "Animated Grid",
|
| 136 |
+
html: `
|
| 137 |
+
<!-- Hero: Animated Grid -->
|
| 138 |
+
<section class="relative overflow-hidden min-h-[60vh] grid place-content-center text-center px-6 py-24 bg-neutral-950">
|
| 139 |
+
<div class="absolute inset-0 bg-[linear-gradient(to_right,rgba(255,255,255,.07)_1px,transparent_1px),linear-gradient(to_bottom,rgba(255,255,255,.07)_1px,transparent_1px)] bg-[size:24px_24px]"></div>
|
| 140 |
+
<div class="absolute inset-0 animate-[pulse_6s_ease-in-out_infinite] bg-gradient-to-br from-white/5 to-transparent"></div>
|
| 141 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 142 |
+
<h1 class="text-4xl md:text-6xl font-extrabold text-white">Grid Hero</h1>
|
| 143 |
+
<p class="mt-4 text-neutral-300 text-lg">Animated grid with light sweep.</p>
|
| 144 |
+
</div>
|
| 145 |
+
</section>
|
| 146 |
+
`.trim(),
|
| 147 |
+
},
|
| 148 |
+
{
|
| 149 |
+
id: "shapes",
|
| 150 |
+
name: "Floating Shapes",
|
| 151 |
+
html: `
|
| 152 |
+
<!-- Hero: Floating Shapes -->
|
| 153 |
+
<section class="relative overflow-hidden min-h-[60vh] grid place-content-center text-center px-6 py-24">
|
| 154 |
+
<div class="absolute inset-0">
|
| 155 |
+
<div class="absolute left-10 top-10 w-8 h-8 rounded-full bg-sky-500/60 animate-[float_7s_ease-in-out_infinite]"></div>
|
| 156 |
+
<div class="absolute right-10 top-14 w-10 h-10 rounded-md bg-purple-500/60 animate-[float_9s_ease-in-out_infinite]"></div>
|
| 157 |
+
<div class="absolute left-1/2 bottom-10 w-12 h-12 rounded-lg bg-pink-500/60 animate-[float_6s_ease-in-out_infinite]"></div>
|
| 158 |
+
</div>
|
| 159 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 160 |
+
<h1 class="text-4xl md:text-6xl font-extrabold text-white">Floating Shapes</h1>
|
| 161 |
+
<p class="mt-4 text-neutral-300 text-lg">Gentle motion with CSS keyframes.</p>
|
| 162 |
+
</div>
|
| 163 |
+
<style>
|
| 164 |
+
@keyframes float { 0%{ transform: translateY(0)} 50%{ transform: translateY(-14px)} 100%{ transform: translateY(0)} }
|
| 165 |
+
</style>
|
| 166 |
+
</section>
|
| 167 |
+
`.trim(),
|
| 168 |
+
},
|
| 169 |
+
{
|
| 170 |
+
id: "lines",
|
| 171 |
+
name: "Moving Lines",
|
| 172 |
+
html: `
|
| 173 |
+
<!-- Hero: Moving Lines -->
|
| 174 |
+
<section class="relative overflow-hidden min-h-[60vh] grid place-content-center text-center px-6 py-24 bg-neutral-950">
|
| 175 |
+
<div class="absolute inset-0 bg-[repeating-linear-gradient(90deg,rgba(255,255,255,.06)_0,rgba(255,255,255,.06)_1px,transparent_1px,transparent_8px)] animate-[scrollX_20s_linear_infinite]"></div>
|
| 176 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 177 |
+
<h1 class="text-4xl md:text-6xl font-extrabold text-white">Moving Lines Hero</h1>
|
| 178 |
+
<p class="mt-4 text-neutral-300 text-lg">Subtle animated lines create motion.</p>
|
| 179 |
+
</div>
|
| 180 |
+
<style>
|
| 181 |
+
@keyframes scrollX { from{ background-position:0 0 } to{ background-position: 100% 0 } }
|
| 182 |
+
</style>
|
| 183 |
+
</section>
|
| 184 |
+
`.trim(),
|
| 185 |
+
},
|
| 186 |
+
{
|
| 187 |
+
id: "orbits",
|
| 188 |
+
name: "Orbits",
|
| 189 |
+
html: `
|
| 190 |
+
<!-- Hero: Orbits -->
|
| 191 |
+
<section class="relative overflow-hidden min-h-[60vh] grid place-content-center text-center px-6 py-24 bg-neutral-950">
|
| 192 |
+
<div class="absolute inset-0 grid place-content-center opacity-30">
|
| 193 |
+
<div class="relative w-64 h-64">
|
| 194 |
+
<div class="absolute inset-0 rounded-full border border-white/20"></div>
|
| 195 |
+
<div class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-52 h-52 rounded-full border border-white/15"></div>
|
| 196 |
+
<div class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-40 h-40 rounded-full border border-white/10"></div>
|
| 197 |
+
</div>
|
| 198 |
+
</div>
|
| 199 |
+
<div class="relative z-10 max-w-3xl mx-auto">
|
| 200 |
+
<h1 class="text-4xl md:text-6xl font-extrabold text-white">Orbit Hero</h1>
|
| 201 |
+
<p class="mt-4 text-neutral-300 text-lg">Concentric orbits with minimal styling.</p>
|
| 202 |
+
</div>
|
| 203 |
+
</section>
|
| 204 |
+
`.trim(),
|
| 205 |
+
},
|
| 206 |
+
];
|
| 207 |
+
|