Put down initial structure of background and cards

This commit is contained in:
GeorgeWebberley 2026-01-28 15:20:04 +01:00
parent 6fe67dcd19
commit 883b3d93f7
7 changed files with 153 additions and 60 deletions

25
Dockerfile Normal file
View file

@ -0,0 +1,25 @@
FROM node:18-alpine AS base
# Install dependencies
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# Build the app
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Production image
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]

View file

@ -1,65 +1,25 @@
import Image from "next/image";
import EventCard from "@/components/EventCard";
import dynamic from 'next/dynamic';
const Starfield = dynamic(() => import('@/components/Starfield'), {
ssr: false
});
export default function Home() {
return (
<div className="flex min-h-screen items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex min-h-screen w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
<main className="min-h-screen flex flex-col items-center justify-center p-8">
<Starfield />
<h1 className="text-white font-mono text-2xl mb-12 tracking-[0.2em] uppercase">
Mission Control // Ground Station
</h1>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 w-full max-w-4xl">
<EventCard
title="ISS Pass: Home"
targetDate={new Date()}
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
</div>
</main>
</div>
</div>
</main>
);
}

32
components/EventCard.tsx Normal file
View file

@ -0,0 +1,32 @@
"use client";
import { motion } from "framer-motion";
import { Rocket } from "lucide-react";
import { EventCardProps } from "@/types/space"
export default function EventCard({ title, targetDate, icon }: EventCardProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="relative p-6 rounded-xl border border-slate-800 bg-slate-900/50 backdrop-blur-md overflow-hidden group"
>
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/20 to-purple-500/20 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none" />
<div className="flex items-center gap-4 mb-4">
<div className="p-2 bg-blue-500/10 rounded-lg text-blue-400">
{icon || <Rocket size={20} />}
</div>
<h3 className="text-slate-300 font-mono tracking-widest uppercase text-sm">{title}</h3>
</div>
<div className="text-4xl font-mono text-white flex gap-2">
<span>02</span><span className="text-slate-500">:</span>
<span>14</span><span className="text-slate-500">:</span>
<span>55</span>
</div>
<p className="text-xs text-slate-500 mt-2 font-mono uppercase">T-Minus to Horizon</p>
</motion.div>
);
}

41
components/Starfield.tsx Normal file
View file

@ -0,0 +1,41 @@
"use client";
import { motion } from "framer-motion";
import { useState } from "react";
import { Star } from "@/types/space"
export default function Starfield() {
const [stars] = useState<Star[]>(() =>
Array.from({ length: 80 }).map((_, i) => ({
id: i,
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`,
size: Math.random() * 2 + 1,
duration: 2 + Math.random() * 3,
delay: Math.random() * 5,
}))
);
return (
<div className="fixed inset-0 z-[-1] bg-[#020617] overflow-hidden">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_50%,_rgba(30,58,138,0.2)_0%,_transparent_50%)]" />
{stars.map((star) => (
<motion.div
key={star.id}
className="absolute bg-white rounded-full shadow-[0_0_5px_white]"
style={{
top: star.top,
left: star.left,
width: star.size,
height: star.size,
}}
animate={{ opacity: [0.2, 0.8, 0.2] }}
transition={{
duration: star.duration,
repeat: Infinity,
delay: star.delay,
}}
/>
))}
</div>
);
}

View file

@ -2,7 +2,7 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
reactCompiler: true,
output: 'standalone',
};
export default nextConfig;

5
types/cards.ts Normal file
View file

@ -0,0 +1,5 @@
interface EventCardProps {
title: string;
targetDate: Date;
icon?: React.ReactNode;
}

30
types/space.ts Normal file
View file

@ -0,0 +1,30 @@
import { ReactNode } from "react";
export interface ISSPass {
id: string;
timestamp: number;
duration: number;
maxElevation: number;
}
export interface CelestialEvent {
title: string;
date: Date;
type: 'iss' | 'eclipse' | 'comet' | 'launch';
description?: string;
}
export interface Star {
id: number;
left: string;
top: string;
size: number;
duration: number;
delay: number;
}
export interface EventCardProps {
title: string;
targetDate: Date;
icon?: ReactNode;
}