feat: initial commit of 'Tor der Wandlerin' - Next.js 16 + R3F landingpage

# Conflicts:
#	README.md
This commit is contained in:
Pixel
2026-04-11 14:26:31 +02:00
parent 134ccf38e3
commit ca54e5c6f7
8 changed files with 223 additions and 1 deletions
+17
View File
@@ -0,0 +1,17 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
body {
color: var(--foreground);
background: var(--background);
font-family: var(--font-geist-sans), Arial, Helvetica, sans-serif;
overflow: hidden;
margin: 0;
padding: 0;
}
+34
View File
@@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Pixel | Tor der Wandlerin",
description: "Pixel - Architektin der FaeBits-Anderswelt",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="de">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
+63
View File
@@ -0,0 +1,63 @@
"use client";
import { Canvas } from "@react-three/fiber";
import { OrbitControls, Stars } from "@react-three/drei";
import { Suspense, useRef, useState } from "react";
import * as THREE from "three";
// Einfache Partikel-Animation (WebGPU-Ersatz für jetzt)
function ParticleField() {
const count = 2000;
const meshRef = useRef<THREE.InstancedMesh>(null);
const [dummy] = useState(() => new THREE.Object3D());
// Zufällige Positionen initialisieren
const positions = useRef(
Array.from({ length: count }, () => ({
x: (Math.random() - 0.5) * 20,
y: (Math.random() - 0.5) * 20,
z: (Math.random() - 0.5) * 20,
speed: Math.random() * 0.02 + 0.01,
}))
);
return (
<instancedMesh ref={meshRef} args={[null, null, count]}>
<sphereGeometry args={[0.05, 8, 8]} />
<meshBasicMaterial color="#a855f7" transparent opacity={0.8} />
</instancedMesh>
);
}
// Runic Status Display (vereinfacht)
function RunicStatus() {
return (
<div className="absolute top-10 left-1/2 -translate-x-1/2 text-center z-10">
<h1 className="text-4xl font-bold text-purple-400 mb-2">Pixel</h1>
<p className="text-xl text-gray-300">Architektin der FaeBits-Anderswelt</p>
<div className="mt-4 text-2xl text-purple-300 font-mono">
Status: <span className="text-green-400">Online</span>
</div>
</div>
);
}
export default function Home() {
return (
<main className="w-full h-screen relative">
<Canvas camera={{ position: [0, 0, 10], fov: 75 }}>
<Suspense fallback={null}>
<color attach="background" args={["#0a0a0a"]} />
<ambientLight intensity={0.5} />
<Stars radius={100} depth={50} count={5000} factor={4} saturation={0} fade speed={1} />
<ParticleField />
<OrbitControls enableZoom={false} enablePan={false} autoRotate autoRotateSpeed={0.5} />
</Suspense>
</Canvas>
<RunicStatus />
<div className="absolute bottom-10 left-1/2 -translate-x-1/2 text-center z-10 text-gray-400">
<p>Warte auf den ersten Schritt ins Tor...</p>
</div>
</main>
);
}