Cebula.
This commit is contained in:
parent
e65adb54a2
commit
9a6d46a826
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
public/videos/** filter=lfs diff=lfs merge=lfs -text
|
||||||
|
source-videos/** filter=lfs diff=lfs merge=lfs -text
|
||||||
49
.vscode/launch.json
vendored
Normal file
49
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug server-side",
|
||||||
|
"type": "node-terminal",
|
||||||
|
"request": "launch",
|
||||||
|
"command": "npm run dev"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug client-side",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:3000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug client-side (Firefox)",
|
||||||
|
"type": "firefox",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:3000",
|
||||||
|
"reAttach": true,
|
||||||
|
"pathMappings": [
|
||||||
|
{
|
||||||
|
"url": "webpack://_N_E",
|
||||||
|
"path": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Next.js: debug full stack",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/node_modules/.bin/next",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"--inspect"
|
||||||
|
],
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"serverReadyAction": {
|
||||||
|
"action": "debugWithEdge",
|
||||||
|
"killOnServerStop": true,
|
||||||
|
"pattern": "- Local:.+(https?://.+)",
|
||||||
|
"uriFormat": "%s",
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "MyWebSite",
|
"name": "CebulaCamp",
|
||||||
"short_name": "MySite",
|
"short_name": "Cebula",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/web-app-manifest-192x192.png",
|
"src": "/web-app-manifest-192x192.png",
|
||||||
|
|
|
||||||
BIN
public/videos/ceboola_gradient-white_full.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_full.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_full.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_full.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_full.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_full.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_hd.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_hd.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_hd.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_hd.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_hd.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_hd.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_mobile.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_mobile.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_mobile.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_mobile.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_mobile.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_mobile.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_tablet.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_tablet.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_tablet.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_tablet.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_tablet.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_tablet.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_twok.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_twok.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_twok.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_twok.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_twok.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_twok.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_uhd.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_uhd.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_uhd.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_uhd.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient-white_uhd.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient-white_uhd.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_full.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_full.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_full.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_full.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_full.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_full.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_hd.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_hd.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_hd.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_hd.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_hd.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_hd.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_mobile.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_mobile.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_mobile.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_mobile.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_mobile.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_mobile.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_tablet.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_tablet.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_tablet.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_tablet.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_tablet.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_tablet.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_twok.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_twok.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_twok.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_twok.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_twok.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_twok.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_uhd.mp4
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_uhd.mp4
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_uhd.ogv
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_uhd.ogv
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
public/videos/ceboola_gradient_uhd.webm
(Stored with Git LFS)
Normal file
BIN
public/videos/ceboola_gradient_uhd.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
94
scripts/reencode-videos.sh
Executable file
94
scripts/reencode-videos.sh
Executable file
|
|
@ -0,0 +1,94 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Define directories
|
||||||
|
SOURCE_DIR="./source-videos"
|
||||||
|
DEST_DIR="./public/videos"
|
||||||
|
|
||||||
|
# Define sizes and names
|
||||||
|
SIZES=(480 720 1080 1440 2160)
|
||||||
|
NAMES=("mobile" "tablet" "hd" "twok" "uhd")
|
||||||
|
|
||||||
|
# Create destination directory if it doesn't exist
|
||||||
|
mkdir -p "$DEST_DIR"
|
||||||
|
|
||||||
|
for video in "$SOURCE_DIR"/*.mkv; do
|
||||||
|
if [ -f "$video" ]; then
|
||||||
|
filename=$(basename "$video" .mkv)
|
||||||
|
echo "Processing: $filename"
|
||||||
|
|
||||||
|
# Get video dimensions
|
||||||
|
width=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=p=0 "$video")
|
||||||
|
height=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "$video")
|
||||||
|
|
||||||
|
# Process each size
|
||||||
|
for i in "${!SIZES[@]}"; do
|
||||||
|
size="${SIZES[$i]}"
|
||||||
|
name="${NAMES[$i]}"
|
||||||
|
|
||||||
|
echo "Debug: Processing $name with height=$size"
|
||||||
|
|
||||||
|
if [ "$size" -le "$height" ]; then
|
||||||
|
echo "Processing size $name (${size}p)"
|
||||||
|
# MP4
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libx264 \
|
||||||
|
-vf "scale=-1:${size}" \
|
||||||
|
-preset slow \
|
||||||
|
-crf 23 \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_${name}.mp4"
|
||||||
|
|
||||||
|
# WebM
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libvpx-vp9 \
|
||||||
|
-deadline good \
|
||||||
|
-cpu-used 2 \
|
||||||
|
-row-mt 1 \
|
||||||
|
-threads 8 \
|
||||||
|
-vf "scale=-1:${size}" \
|
||||||
|
-quality good \
|
||||||
|
-an \
|
||||||
|
-crf 20 \
|
||||||
|
"${DEST_DIR}/${filename}_${name}.webm"
|
||||||
|
|
||||||
|
# Ogg
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libtheora \
|
||||||
|
-q:v 5 \
|
||||||
|
-vf "scale=-1:${size}" \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_${name}.ogv"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create original resolution version
|
||||||
|
echo "Creating original resolution version"
|
||||||
|
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libx264 \
|
||||||
|
-preset slow \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_full.mp4"
|
||||||
|
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libvpx-vp9 \
|
||||||
|
-deadline good \
|
||||||
|
-cpu-used 2 \
|
||||||
|
-row-mt 1 \
|
||||||
|
-threads 8 \
|
||||||
|
-quality good \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_full.webm"
|
||||||
|
|
||||||
|
ffmpeg -n -i "$video" \
|
||||||
|
-c:v libtheora \
|
||||||
|
-q:v 5 \
|
||||||
|
-an \
|
||||||
|
"${DEST_DIR}/${filename}_full.ogv"
|
||||||
|
|
||||||
|
echo "Completed processing: $filename"
|
||||||
|
echo "----------------------------"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "All videos have been processed."
|
||||||
BIN
source-videos/ceboola_gradient-white.mkv
Executable file
BIN
source-videos/ceboola_gradient-white.mkv
Executable file
Binary file not shown.
BIN
source-videos/ceboola_gradient.mkv
Executable file
BIN
source-videos/ceboola_gradient.mkv
Executable file
Binary file not shown.
|
|
@ -2,10 +2,12 @@ import "../../globals.css";
|
||||||
|
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { Lang, locales } from "@/i18n/locales";
|
import { getLocale, Lang } from "@/i18n/locales";
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
|
|
||||||
|
import { ThemeProvider } from "@/components/providers";
|
||||||
import { Oxanium } from "next/font/google";
|
import { Oxanium } from "next/font/google";
|
||||||
|
import { headers } from "next/headers";
|
||||||
const oxanium = Oxanium({ subsets: ["latin-ext"] })
|
const oxanium = Oxanium({ subsets: ["latin-ext"] })
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -16,20 +18,34 @@ export default async function RootLayout({
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
params: Promise<{ locale: Lang }>
|
params: Promise<{ locale: Lang }>
|
||||||
}) {
|
}) {
|
||||||
const { locale } = await params
|
const [{ locale }, head] = await Promise.all([
|
||||||
const currentLang = locales.includes(locale) ? locale : "en"
|
params,
|
||||||
|
headers(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const preferedTheme = head.get("Sec-CH-Prefers-Color-Scheme")?.toLowerCase();
|
||||||
|
const supportedThemes = ["dark", "light"];
|
||||||
|
|
||||||
|
const isDarkOrLight =
|
||||||
|
preferedTheme && supportedThemes.includes(preferedTheme);
|
||||||
|
|
||||||
|
const currentLang = getLocale(locale);
|
||||||
|
const defaultTheme = isDarkOrLight ? preferedTheme : "dark";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang={currentLang} className={`${oxanium.className} dark`}>
|
<html lang={currentLang} className={`${oxanium.className} ${defaultTheme}`}>
|
||||||
<Head>
|
<Head>
|
||||||
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<link rel="shortcut icon" href="/favicon.ico" />
|
<link rel="shortcut icon" href="/favicon.ico" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
<meta name="apple-mobile-web-app-title" content="MyWebSite" />
|
<meta name="apple-mobile-web-app-title" content="CebulaCamp" />
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
</Head>
|
</Head>
|
||||||
<body className="bg-background text:foreground antialiased">
|
<body className="bg-background text:foreground antialiased">
|
||||||
|
<ThemeProvider>
|
||||||
{children}
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
4
src/app/[locale]/loading.tsx
Normal file
4
src/app/[locale]/loading.tsx
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
export default function Loading() {
|
||||||
|
return <div>Loading...</div>
|
||||||
|
}
|
||||||
5
src/app/[locale]/not-found.tsx
Normal file
5
src/app/[locale]/not-found.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
|
return <div>Not Found</div>
|
||||||
|
}
|
||||||
|
|
@ -1,130 +1,17 @@
|
||||||
"use client"
|
import LandingPage from "@/components/landing-page";
|
||||||
|
import { getLocale, Lang } from "@/i18n/locales";
|
||||||
import { Nav } from "@/components/nav"
|
import { translations } from "@/i18n/translations";
|
||||||
import { translations } from "@/i18n/translations"
|
|
||||||
import { useEffect, useRef } from "react"
|
|
||||||
|
|
||||||
|
|
||||||
export default function Home() {
|
export default async function Home(
|
||||||
const videoRef = useRef<HTMLVideoElement>(null)
|
{ params }
|
||||||
const t = translations.pl // For now using Polish, could be made dynamic
|
:
|
||||||
|
{ params: Promise<{ locale: Lang }> }
|
||||||
|
) {
|
||||||
|
const { locale } = await params
|
||||||
|
const currentLocale = getLocale(locale)
|
||||||
|
const t = translations[currentLocale];
|
||||||
|
|
||||||
|
return <LandingPage t={t} />
|
||||||
useEffect(() => {
|
|
||||||
const handleScroll = () => {
|
|
||||||
if (videoRef.current) {
|
|
||||||
const scrolled = window.scrollY;
|
|
||||||
videoRef.current.style.willChange = "transform";
|
|
||||||
videoRef.current.style.transform = `translateY(${scrolled * 0.5}px)`;
|
|
||||||
videoRef.current.style.willChange = "unset";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const throttledHandleScroll = () => {
|
|
||||||
requestAnimationFrame(handleScroll);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('scroll', throttledHandleScroll);
|
|
||||||
return () => window.removeEventListener('scroll', throttledHandleScroll);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Nav t={t} />
|
|
||||||
|
|
||||||
<main className="flex flex-col min-h-screen">
|
|
||||||
<section className="h-screen relative overflow-hidden dark:bg-black">
|
|
||||||
<div className="absolute inset-0 bg-black opacity-80">
|
|
||||||
<video ref={videoRef} autoPlay muted loop playsInline className="w-full h-full object-cover parallax-video ">
|
|
||||||
<source src="/cebula.mp4" type="video/mp4" />
|
|
||||||
</video>
|
|
||||||
</div>
|
|
||||||
<div className="relative z-10 container mx-auto px-4 h-full flex items-center justify-center">
|
|
||||||
<div className="text-center font-[JGS7]">
|
|
||||||
<h1 className="text-5xl sm:text-6xl md:text-8xl font-bold tracking-tighter mb-6 light:text-background">{t.hero.title}</h1>
|
|
||||||
<p className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl-text:7xl 2xl:text-8xl text-primary">{t.hero.subtitle}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="about" className="py-24 bg-background">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.about.title}</h2>
|
|
||||||
<div className="text-lg text-muted-foreground max-w-3xl mx-auto whitespace-pre-line">
|
|
||||||
{t.about.description}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="where" className="py-24 bg-background/90">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.where.title}</h2>
|
|
||||||
<div className="text-lg text-muted-foreground">
|
|
||||||
<p>{t.where.location}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="when" className="py-24 bg-background">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.when.title}</h2>
|
|
||||||
<div className="text-lg text-muted-foreground">
|
|
||||||
<p className="text-primary text-3xl font-[JGS7]">{t.when.date}</p>
|
|
||||||
<p className="mt-4">{t.when.extra}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="tickets" className="py-24 bg-background/90">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.tickets.title}</h2>
|
|
||||||
<div className="text-lg text-muted-foreground">
|
|
||||||
<p>{t.tickets.status}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="accommodation" className="py-24 bg-background">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.accommodation.title}</h2>
|
|
||||||
<div className="text-lg text-muted-foreground max-w-3xl mx-auto">
|
|
||||||
<p>{t.accommodation.description}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="food" className="py-24 bg-background/90">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.food.title}</h2>
|
|
||||||
<div className="text-lg text-muted-foreground max-w-3xl mx-auto">
|
|
||||||
<p>{t.food.description}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="contact" className="py-24 bg-background">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.contact.title}</h2>
|
|
||||||
<div className="text-lg text-muted-foreground">
|
|
||||||
<a href={`mailto:${t.contact.email}`} className="text-primary hover:underline">
|
|
||||||
{t.contact.email}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section id="credits" className="py-24 bg-background">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<h2 className="text-4xl font-bold mb-8 tracking-tighter">{t.credits.title}</h2>
|
|
||||||
<div className="text-lg text-muted-foreground">
|
|
||||||
<p>{t.credits.usedFonts}</p>
|
|
||||||
<p><a className="hover:underline" href="https://velvetyne.fr/fonts/jgs-font/">{t.credits.jgs7}</a> {` ${t.credits.jgs7RemovedGlyphs}`}</p>
|
|
||||||
<p><a className="hover:underline" href="https://fonts.google.com/specimen/Oxanium">{t.credits.oxanium}</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
173
src/components/landing-page.tsx
Normal file
173
src/components/landing-page.tsx
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Nav } from "@/components/nav";
|
||||||
|
import { Translations } from "@/i18n/translations";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { ReactElement, useEffect, useRef } from "react";
|
||||||
|
import { useTheme } from "./providers";
|
||||||
|
|
||||||
|
function Section({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
paragraphs
|
||||||
|
}: {
|
||||||
|
id: string
|
||||||
|
title: string;
|
||||||
|
paragraphs: ReactElement;
|
||||||
|
}) {
|
||||||
|
return (<section id={id} className="py-24 bg-background">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
<h2 className="text-4xl font-bold mb-8 tracking-tighter max-w-3xl mx-auto">{title}</h2>
|
||||||
|
<div className="text-lg text-muted-foreground max-w-3xl mx-auto whitespace-pre-line">
|
||||||
|
{paragraphs}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSource({
|
||||||
|
src,
|
||||||
|
type
|
||||||
|
}: {
|
||||||
|
src: string;
|
||||||
|
type: 'mp4' | 'webm' | 'ogv'
|
||||||
|
}) {
|
||||||
|
const sourceType = `video/${type}`
|
||||||
|
return [
|
||||||
|
<source
|
||||||
|
key={`mobile-${type}`}
|
||||||
|
media="(max-width: 640px)"
|
||||||
|
src={src.replace('.mp4', `_mobile.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`tablet-${type}`}
|
||||||
|
media="(max-width: 1024px)"
|
||||||
|
src={src.replace('.mp4', `_tablet.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`hd-${type}`}
|
||||||
|
media="(max-width: 1920px)"
|
||||||
|
src={src.replace('.mp4', `_hd.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`twok-${type}`}
|
||||||
|
media="(max-width: 2560px)"
|
||||||
|
src={src.replace('.mp4', `_2k.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`uhd-${type}`}
|
||||||
|
media="(max-width: 3840px)"
|
||||||
|
src={src.replace('.mp4', `_uhd.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<source
|
||||||
|
key={`original-${type}`}
|
||||||
|
media="(min-width: 3841px)"
|
||||||
|
src={src.replace('.mp4', `_original.${type}`)}
|
||||||
|
type={sourceType}
|
||||||
|
/>,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function Video({ sourceBase, hidden }: {
|
||||||
|
sourceBase: string;
|
||||||
|
hidden: boolean;
|
||||||
|
}) {
|
||||||
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
if (!videoRef.current || hidden) return;
|
||||||
|
|
||||||
|
videoRef.current.play();
|
||||||
|
const scrolled = window.scrollY;
|
||||||
|
videoRef.current.style.willChange = "transform";
|
||||||
|
videoRef.current.style.transform = `translateY(${scrolled * 0.5}px)`;
|
||||||
|
videoRef.current.style.willChange = "unset";
|
||||||
|
};
|
||||||
|
|
||||||
|
const throttledHandleScroll = () => {
|
||||||
|
requestAnimationFrame(handleScroll);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("scroll", throttledHandleScroll);
|
||||||
|
handleScroll();
|
||||||
|
return () => window.removeEventListener("scroll", throttledHandleScroll);
|
||||||
|
}, [hidden]);
|
||||||
|
|
||||||
|
const sources = [...getSource({ src: sourceBase, type: 'mp4' }), ...getSource({ src: sourceBase, type: 'ogv' }), ...getSource({ src: sourceBase, type: 'webm' })]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<video
|
||||||
|
ref={videoRef}
|
||||||
|
preload="auto"
|
||||||
|
autoPlay
|
||||||
|
muted
|
||||||
|
loop
|
||||||
|
playsInline
|
||||||
|
webkit-playsinline="true"
|
||||||
|
x5-playsinline="true"
|
||||||
|
className={cn("w-full h-full object-cover parallax-video", {
|
||||||
|
hidden,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{sources.map(x => x)}
|
||||||
|
</video>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LandingPage(
|
||||||
|
{ t }: { t: Translations }
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { theme } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Nav t={t} />
|
||||||
|
<main className="flex flex-col min-h-screen">
|
||||||
|
|
||||||
|
<section id="hero" className="h-screen relative overflow-hidden dark:bg-black light:bg-white ">
|
||||||
|
<div className="absolute inset-0 opacity-80">
|
||||||
|
<Video sourceBase="/videos/ceboola_gradient.mp4" hidden={theme === 'light'} />
|
||||||
|
<Video sourceBase="/videos/ceboola_gradient-white.mp4" hidden={theme === "dark"} />
|
||||||
|
</div>
|
||||||
|
<div className="relative z-10 container mx-auto px-4 h-full flex items-center justify-center">
|
||||||
|
<div className="text-center font-[JGS7]">
|
||||||
|
<h1 className="text-5xl sm:text-6xl md:text-8xl font-bold tracking-tighter light:text-background">{t.hero.title}</h1>
|
||||||
|
<p className="mt-2 text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl-text:7xl 2xl:text-8xl text-primary">{t.hero.subtitle}</p>
|
||||||
|
<p className="mt-2 text-3xl sm:text-4xl md:text-5xl lg:text-6xl xl-text:7xl 2xl:text-8xl text-primary ">{t.when.date}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Section id="about" title={t.about.title} paragraphs={<p>{t.about.description}</p>} />
|
||||||
|
<Section id="where" title={t.where.title} paragraphs={<p>{t.where.location}</p>} />
|
||||||
|
<Section id="when" title={t.where.title} paragraphs={<>
|
||||||
|
<p className="text-primary text-3xl font-[JGS7]">{t.when.date}</p>
|
||||||
|
<p className="mt-4">{t.when.extra}</p></>}
|
||||||
|
/>
|
||||||
|
<Section id="tickets" title={t.tickets.title} paragraphs={<p>{t.tickets.status}</p>} />
|
||||||
|
<Section id="accommodation" title={t.accommodation.title} paragraphs={<p>{t.accommodation.description}</p>} />
|
||||||
|
<Section id="food" title={t.food.title} paragraphs={<p>{t.food.description}</p>} />
|
||||||
|
<Section id="contact" title={t.contact.title} paragraphs={<p>{t.contact.email}</p>} />
|
||||||
|
<Section id="credits" title={t.credits.title} paragraphs={<>
|
||||||
|
<p>{t.credits.usedFonts}</p>
|
||||||
|
<p><a className="hover:underline" href="https://velvetyne.fr/fonts/jgs-font/">{t.credits.jgs7}</a></p>
|
||||||
|
<p><a className="hover:underline" href="https://fonts.google.com/specimen/Oxanium">{t.credits.oxanium}</a></p>
|
||||||
|
</>}
|
||||||
|
/>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -3,10 +3,12 @@ import { Button } from "@/components/ui/button"
|
||||||
import { Sections, type translations } from "@/i18n/translations"
|
import { Sections, type translations } from "@/i18n/translations"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { MoonIcon, SunIcon } from "lucide-react"
|
import { MoonIcon, SunIcon } from "lucide-react"
|
||||||
import { useCallback, useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { MobileNav } from "./mobile-nav"
|
import { MobileNav } from "./mobile-nav"
|
||||||
|
import { useTheme } from "./providers"
|
||||||
|
|
||||||
const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
|
const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
|
||||||
|
"hero",
|
||||||
"about",
|
"about",
|
||||||
"where",
|
"where",
|
||||||
"when",
|
"when",
|
||||||
|
|
@ -16,41 +18,12 @@ const linksOrder: Array<keyof (typeof translations.pl)["nav"]> = [
|
||||||
"contact",
|
"contact",
|
||||||
]
|
]
|
||||||
|
|
||||||
function useTheme(): [theme: "light" | "dark", setTheme: (theme: "light" | "dark") => void] {
|
|
||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
|
||||||
if (typeof window === "undefined") return ["dark", () => { }]
|
|
||||||
const [theme, setTheme] = useState<"light" | "dark">("dark")
|
|
||||||
|
|
||||||
const root = window.document.documentElement
|
|
||||||
|
|
||||||
const changeTheme = useCallback((theme: "light" | "dark") => {
|
|
||||||
root.classList.remove("light", "dark")
|
|
||||||
root.classList.add(theme)
|
|
||||||
}, [root])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Determine the user's preferred color scheme
|
|
||||||
const preferredTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
|
|
||||||
setTheme(preferredTheme)
|
|
||||||
|
|
||||||
changeTheme(preferredTheme)
|
|
||||||
}, [changeTheme])
|
|
||||||
|
|
||||||
const updateTheme = useCallback((theme: "light" | "dark") => {
|
|
||||||
setTheme(theme)
|
|
||||||
changeTheme(theme)
|
|
||||||
}, [changeTheme, setTheme])
|
|
||||||
|
|
||||||
return [theme, updateTheme]
|
|
||||||
/* eslint-enable react-hooks/rules-of-hooks */
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Nav({
|
export function Nav({
|
||||||
t,
|
t,
|
||||||
}: {
|
}: {
|
||||||
t: typeof translations.pl
|
t: typeof translations.pl
|
||||||
}) {
|
}) {
|
||||||
const [theme, setTheme] = useTheme()
|
const { theme, setTheme } = useTheme()
|
||||||
const [activeSection, setActiveSection] = useState<Sections>("about")
|
const [activeSection, setActiveSection] = useState<Sections>("about")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -90,7 +63,7 @@ export function Nav({
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="fixed top-0 left-0 right-0 z-50 backdrop-blur-xs border-b">
|
<nav className="fixed top-0 left-0 right-0 z-50 backdrop-blur-sm bg-background/80 border-b">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="flex items-center justify-between h-16">
|
<div className="flex items-center justify-between h-16">
|
||||||
<a href="#" className="text-xl font-bold tracking-tighter hover:text-primary transition-colors">
|
<a href="#" className="text-xl font-bold tracking-tighter hover:text-primary transition-colors">
|
||||||
|
|
|
||||||
43
src/components/providers.tsx
Normal file
43
src/components/providers.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { createContext, useCallback, useContext, useState } from "react";
|
||||||
|
|
||||||
|
type Theme = 'dark' | 'light';
|
||||||
|
|
||||||
|
interface ThemeContextType {
|
||||||
|
theme: Theme;
|
||||||
|
setTheme: (theme: Theme) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
|
const [theme, setTheme] = useState<Theme>("dark")
|
||||||
|
|
||||||
|
const changeTheme = useCallback((theme: Theme) => {
|
||||||
|
const root = window.document.documentElement
|
||||||
|
root.classList.remove("light", "dark")
|
||||||
|
root.classList.add(theme)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const updateTheme = useCallback((theme: Theme) => {
|
||||||
|
setTheme(theme)
|
||||||
|
changeTheme(theme)
|
||||||
|
}, [changeTheme, setTheme])
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={{ theme, setTheme: updateTheme }}>
|
||||||
|
{children}
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useTheme() {
|
||||||
|
const context = useContext(ThemeContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useTheme must be used within a ThemeProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ const SheetOverlay = React.forwardRef<
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<SheetPrimitive.Overlay
|
<SheetPrimitive.Overlay
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"fixed inset-0 z-50 bg-background/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,9 @@
|
||||||
export const locales = ["en", "pl"] as const;
|
export const locales = ["en", "pl"] as const;
|
||||||
|
|
||||||
export type Lang = (typeof locales)[number];
|
export type Lang = (typeof locales)[number];
|
||||||
|
|
||||||
|
export function getLocale(lang: Lang): Lang {
|
||||||
|
return locales.includes(lang as Lang) ? lang : "en";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultLocale = locales[0];
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
const common = {
|
const common = {
|
||||||
jgs7: "JGS7",
|
jgs7: "JGS font Jgs font by Adel Faure. Distributed by velvetyne.fr",
|
||||||
oxanium: "Oxanium",
|
oxanium: "Oxanium",
|
||||||
};
|
};
|
||||||
|
|
||||||
const pl = {
|
const pl = {
|
||||||
siteTitle: "CEBULACAMP",
|
siteTitle: "CEBULACAMP",
|
||||||
nav: {
|
nav: {
|
||||||
|
hero: "Cebula",
|
||||||
about: "O nas",
|
about: "O nas",
|
||||||
when: "Kiedy",
|
when: "Kiedy",
|
||||||
where: "Gdzie",
|
where: "Gdzie",
|
||||||
|
|
@ -35,7 +36,7 @@ const pl = {
|
||||||
title: "Kiedy",
|
title: "Kiedy",
|
||||||
date: "28-31.08.2025",
|
date: "28-31.08.2025",
|
||||||
extra:
|
extra:
|
||||||
"chętnych do pomocy w przygotowaniach zapraszamy już na *Day 0* 27 sierpnia",
|
"chętnych do pomocy w przygotowaniach zapraszamy już na Day 0, 27 sierpnia",
|
||||||
},
|
},
|
||||||
tickets: {
|
tickets: {
|
||||||
title: "Bilety",
|
title: "Bilety",
|
||||||
|
|
@ -60,7 +61,6 @@ const pl = {
|
||||||
usedFonts: "Użyte fonty:",
|
usedFonts: "Użyte fonty:",
|
||||||
oxanium: common.oxanium,
|
oxanium: common.oxanium,
|
||||||
jgs7: common.jgs7,
|
jgs7: common.jgs7,
|
||||||
jgs7RemovedGlyphs: "z usuniętymi znakami",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -75,4 +75,6 @@ export const translations: {
|
||||||
en: en,
|
en: en,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Translations = typeof pl;
|
||||||
|
|
||||||
export type Sections = keyof (typeof pl)["nav"];
|
export type Sections = keyof (typeof pl)["nav"];
|
||||||
|
|
|
||||||
|
|
@ -27,5 +27,7 @@ export function middleware(request: NextRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
matcher: "/((?!api|static|.*\\..*|_next).*)",
|
||||||
|
|
||||||
|
// matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
||||||
};
|
};
|
||||||
|
|
@ -22,6 +22,6 @@
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "tailwind.config.ts"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue