Initial Commit
21
components.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "src/styles/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
@ -9,9 +9,16 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^11.17.0",
|
||||
"lucide-react": "^0.471.0",
|
||||
"next": "15.1.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"next": "15.1.4"
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
|
BIN
public/images/hero-background.jpg
Normal file
After Width: | Height: | Size: 3.9 MiB |
BIN
public/images/portfolio-0.jpg
Normal file
After Width: | Height: | Size: 19 MiB |
BIN
public/images/portfolio-1.jpg
Normal file
After Width: | Height: | Size: 21 MiB |
BIN
public/images/portfolio-2.jpg
Normal file
After Width: | Height: | Size: 17 MiB |
BIN
public/images/portfolio-3.jpg
Normal file
After Width: | Height: | Size: 16 MiB |
BIN
public/images/portfolio-4.jpg
Normal file
After Width: | Height: | Size: 4.3 MiB |
BIN
public/images/portfolio-5.jpg
Normal file
After Width: | Height: | Size: 7.3 MiB |
BIN
public/images/portfolio-6.jpg
Normal file
After Width: | Height: | Size: 3.3 MiB |
5
src/components/ui/aspect-ratio.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
|
||||
|
||||
const AspectRatio = AspectRatioPrimitive.Root
|
||||
|
||||
export { AspectRatio }
|
76
src/components/ui/card.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Card = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"bg-card text-card-foreground shadow",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Card.displayName = "Card"
|
||||
|
||||
const CardHeader = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardHeader.displayName = "CardHeader"
|
||||
|
||||
const CardTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn("font-semibold leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardTitle.displayName = "CardTitle"
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<p
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardDescription.displayName = "CardDescription"
|
||||
|
||||
const CardContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
))
|
||||
CardContent.displayName = "CardContent"
|
||||
|
||||
const CardFooter = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex items-center p-6 pt-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
CardFooter.displayName = "CardFooter"
|
||||
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
6
src/lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
@ -3,7 +3,9 @@ import { Html, Head, Main, NextScript } from "next/document";
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<Head>
|
||||
<meta name="color-scheme" content="dark" />
|
||||
</Head>
|
||||
<body className="antialiased">
|
||||
<Main />
|
||||
<NextScript />
|
||||
|
@ -1,114 +1,724 @@
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Montserrat, Playfair_Display } from "next/font/google";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import React from "react";
|
||||
import Head from 'next/head';
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
const playfair = Playfair_Display({
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-playfair",
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
const montserrat = Montserrat({
|
||||
subsets: ["latin"],
|
||||
variable: "--font-montserrat",
|
||||
});
|
||||
|
||||
const MotionCard = motion(Card);
|
||||
|
||||
const focusStyles = `
|
||||
focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-black
|
||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-black
|
||||
`;
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div
|
||||
className={`${geistSans.variable} ${geistMono.variable} grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]`}
|
||||
>
|
||||
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
||||
src/pages/index.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>Save and see your changes instantly.</li>
|
||||
</ol>
|
||||
const [isScrolled, setIsScrolled] = React.useState(false);
|
||||
const [isMenuVisible, setIsMenuVisible] = React.useState(false);
|
||||
const lastScrollY = React.useRef(0);
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
React.useEffect(() => {
|
||||
const updateScroll = () => {
|
||||
const currentScrollY = window.scrollY;
|
||||
setIsScrolled(currentScrollY > 100);
|
||||
|
||||
// Always hide menu when at the very top
|
||||
if (currentScrollY === 0) {
|
||||
setIsMenuVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show menu when scrolling up and past initial threshold
|
||||
if (currentScrollY < lastScrollY.current && currentScrollY > 100) {
|
||||
setIsMenuVisible(true);
|
||||
}
|
||||
// Hide menu when scrolling down
|
||||
else if (currentScrollY > lastScrollY.current) {
|
||||
setIsMenuVisible(false);
|
||||
}
|
||||
|
||||
lastScrollY.current = currentScrollY;
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', updateScroll);
|
||||
updateScroll(); // Initial check
|
||||
|
||||
return () => window.removeEventListener('scroll', updateScroll);
|
||||
}, []);
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
const scrollToSection = (sectionId: string) => {
|
||||
const element = document.getElementById(sectionId);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
<>
|
||||
<Head>
|
||||
<title>Johannes Behrens - International Model</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Johannes Behrens is an international model based in Hamburg, Germany. Specializing in commercial, fitness, and editorial modeling with a dynamic and charismatic presence."
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</Head>
|
||||
<motion.main
|
||||
className={`${playfair.variable} ${montserrat.variable} min-h-screen font-sans bg-black`}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.7, ease: [0.32, 0.72, 0, 1] }}
|
||||
>
|
||||
{/* Skip Link */}
|
||||
<a
|
||||
href="#main-content"
|
||||
className={`
|
||||
sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4
|
||||
bg-white text-black px-4 py-2 z-50 ${focusStyles}
|
||||
`}
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
Skip to main content
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
|
||||
{/* Menu Bar */}
|
||||
<header>
|
||||
<motion.nav
|
||||
className="fixed top-0 left-0 right-0 z-50 bg-black/90 backdrop-blur-md border-b border-white/10"
|
||||
initial={{ y: -100 }}
|
||||
animate={{ y: isMenuVisible ? 0 : -100 }}
|
||||
transition={{ duration: 0.3, ease: [0.32, 0.72, 0, 1] }}
|
||||
role="navigation"
|
||||
aria-label="Main navigation"
|
||||
>
|
||||
<div className="container mx-auto px-4 sm:px-6 md:px-8 h-16 flex items-center justify-between">
|
||||
<motion.button
|
||||
className="flex items-center h-full"
|
||||
onClick={scrollToTop}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
aria-label="Back to top"
|
||||
>
|
||||
<div className="flex flex-col leading-[0.8]">
|
||||
<span className="font-playfair text-xl text-white font-black tracking-tight">Johannes</span>
|
||||
<span className="font-playfair text-xl text-white font-black tracking-tight pl-4">Behrens</span>
|
||||
</div>
|
||||
</motion.button>
|
||||
<div className="flex items-center gap-8" role="menubar">
|
||||
<motion.button
|
||||
className={`font-montserrat text-sm text-white/60 hover:text-white transition-colors ${focusStyles}`}
|
||||
onClick={() => scrollToSection('about')}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
aria-label="Go to about section"
|
||||
role="menuitem"
|
||||
tabIndex={0}
|
||||
>
|
||||
About
|
||||
</motion.button>
|
||||
<motion.button
|
||||
className="font-montserrat text-sm text-white/60 hover:text-white transition-colors"
|
||||
onClick={() => scrollToSection('portfolio')}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
aria-label="Go to portfolio section"
|
||||
role="menuitem"
|
||||
>
|
||||
Portfolio
|
||||
</motion.button>
|
||||
<motion.button
|
||||
className="font-montserrat text-sm text-white/60 hover:text-white transition-colors"
|
||||
onClick={() => scrollToSection('contact')}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
aria-label="Go to contact section"
|
||||
role="menuitem"
|
||||
>
|
||||
Contact
|
||||
</motion.button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.nav>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<div id="main-content" tabIndex={-1}>
|
||||
{/* Hero Section */}
|
||||
<section
|
||||
className="relative h-[100svh] bg-black overflow-hidden"
|
||||
aria-label="Hero section"
|
||||
>
|
||||
{/* Background Image */}
|
||||
<motion.div
|
||||
className="absolute inset-0 w-full md:w-[80%] left-0 md:left-[-15%]"
|
||||
initial={{ opacity: 0, scale: 1.1 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 1.4, ease: [0.32, 0.72, 0, 1], delay: 0.3 }}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{/* Mobile Image */}
|
||||
<div className="relative w-full h-full md:hidden">
|
||||
<Image
|
||||
src="/images/hero-background.jpg"
|
||||
alt="Johannes Behrens, professional model, standing confidently in a fashion pose"
|
||||
fill
|
||||
className="object-cover object-[center_15%]"
|
||||
priority
|
||||
quality={85}
|
||||
sizes="100vw"
|
||||
loading="eager"
|
||||
fetchPriority="high"
|
||||
/>
|
||||
<motion.div
|
||||
className="absolute inset-0 bg-gradient-to-b from-black/80 via-black/30 to-black/80"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, ease: [0.32, 0.72, 0, 1], delay: 0.6 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Desktop Image */}
|
||||
<div className="relative w-full h-full hidden md:block">
|
||||
<Image
|
||||
src="/images/hero-background.jpg"
|
||||
alt="Johannes Behrens, professional model, standing confidently in a fashion pose"
|
||||
fill
|
||||
className="object-cover"
|
||||
priority
|
||||
quality={85}
|
||||
sizes="(max-width: 768px) 100vw, 80vw"
|
||||
loading="eager"
|
||||
fetchPriority="high"
|
||||
/>
|
||||
<motion.div
|
||||
className="absolute inset-0 bg-gradient-to-r from-black via-black/20 to-transparent"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, ease: [0.32, 0.72, 0, 1], delay: 0.6 }}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="absolute inset-0 flex items-center md:justify-start">
|
||||
<div className="w-full md:w-[80%] px-6 sm:px-8 md:pl-[25%] md:pr-0 lg:pl-[50%]">
|
||||
<div className="relative">
|
||||
{/* Main Title */}
|
||||
<motion.div
|
||||
className="relative z-20 text-center md:text-left"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, ease: [0.32, 0.72, 0, 1], delay: 0.7 }}
|
||||
>
|
||||
<div className="mix-blend-difference">
|
||||
<motion.div
|
||||
className="overflow-hidden mb-4"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.7, ease: [0.32, 0.72, 0, 1], delay: 0.9 }}
|
||||
>
|
||||
<div className="font-montserrat tracking-[0.3em] md:tracking-[0.5em] text-white/90 text-xs sm:text-sm uppercase font-medium">
|
||||
International Model
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.h1
|
||||
className="font-playfair text-[4.5rem] sm:text-[5rem] md:text-[7rem] lg:text-[8rem] font-black text-white leading-[0.8] tracking-tight"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1], delay: 1 }}
|
||||
>
|
||||
Johannes
|
||||
</motion.h1>
|
||||
<span className="font-playfair text-[3.5rem] sm:text-[4rem] md:text-[5rem] lg:text-[6rem] font-black text-white md:leading-[0.7] tracking-tight md:pl-14">
|
||||
Behrens
|
||||
</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Bottom Content */}
|
||||
<motion.div
|
||||
className="mt-8 md:mt-12 mix-blend-difference"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, ease: [0.32, 0.72, 0, 1], delay: 1.2 }}
|
||||
>
|
||||
<div className="grid grid-cols-3 gap-3 md:gap-4 text-white/80 text-center md:text-left">
|
||||
<div className="space-y-2">
|
||||
<h2 className="font-montserrat text-[10px] md:text-xs tracking-[0.2em] uppercase font-semibold">Based in</h2>
|
||||
<p className="font-montserrat text-lg md:text-xl font-medium">Hamburg</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h2 className="font-montserrat text-[10px] md:text-xs tracking-[0.2em] uppercase font-semibold">Height</h2>
|
||||
<p className="font-montserrat text-lg md:text-xl font-medium">183 cm</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h2 className="font-montserrat text-[10px] md:text-xs tracking-[0.2em] uppercase font-semibold">Bookings</h2>
|
||||
<p className="font-montserrat text-lg md:text-xl font-medium">Worldwide</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Mobile Categories */}
|
||||
<motion.div
|
||||
className="mt-8 text-center md:hidden"
|
||||
initial={{ opacity: 0, y: 15 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.7, ease: [0.32, 0.72, 0, 1], delay: 1.4 }}
|
||||
>
|
||||
<p className="font-montserrat text-[10px] tracking-[0.3em] text-white/60 uppercase font-medium">
|
||||
Commercial • Fitness
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scroll Indicator/To Top Button */}
|
||||
<motion.div
|
||||
className="fixed bottom-6 md:bottom-8 right-6 md:right-8 z-50"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.7, ease: [0.32, 0.72, 0, 1], delay: 1.6 }}
|
||||
>
|
||||
<motion.button
|
||||
onClick={scrollToTop}
|
||||
className={`group relative flex items-center gap-2 h-10 ${focusStyles}`}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
transition={{ duration: 0.3, ease: [0.32, 0.72, 0, 1] }}
|
||||
aria-label="Scroll to top"
|
||||
tabIndex={0}
|
||||
>
|
||||
{/* Background */}
|
||||
<motion.div
|
||||
className="absolute right-0 bg-black/50 backdrop-blur-md rounded-full"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{
|
||||
opacity: isScrolled ? 1 : 0,
|
||||
scale: isScrolled ? 1 : 0.8,
|
||||
width: isScrolled ? 40 : 120,
|
||||
}}
|
||||
transition={{ duration: 0.3, ease: [0.32, 0.72, 0, 1] }}
|
||||
style={{
|
||||
height: 40,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Scroll Text and Line */}
|
||||
<motion.div
|
||||
className="flex items-center gap-2 px-3"
|
||||
animate={{
|
||||
opacity: isScrolled ? 0 : 1,
|
||||
pointerEvents: isScrolled ? "none" : "auto",
|
||||
}}
|
||||
transition={{ duration: 0.2, ease: [0.32, 0.72, 0, 1] }}
|
||||
>
|
||||
<div className="font-montserrat tracking-[0.3em] text-white/60 text-[10px] uppercase font-medium mix-blend-difference">
|
||||
Scroll
|
||||
</div>
|
||||
<div className="w-[40px] md:w-[50px] h-px bg-white/60 origin-right mix-blend-difference" />
|
||||
</motion.div>
|
||||
|
||||
{/* Arrow Up */}
|
||||
<motion.div
|
||||
className="absolute right-0 flex items-center justify-center w-10 h-10"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{
|
||||
opacity: isScrolled ? 1 : 0,
|
||||
pointerEvents: isScrolled ? "auto" : "none",
|
||||
}}
|
||||
transition={{ duration: 0.2, ease: [0.32, 0.72, 0, 1] }}
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="text-white/60"
|
||||
>
|
||||
<path
|
||||
d="M7 1L7 13M7 1L13 7M7 1L1 7"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</motion.div>
|
||||
</motion.button>
|
||||
</motion.div>
|
||||
</section>
|
||||
|
||||
{/* Details Section */}
|
||||
<section id="about" className="py-16 sm:py-20 md:py-24 lg:py-32 bg-black border-t border-white/10">
|
||||
<div className="container mx-auto px-4 sm:px-6 md:px-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-12 md:gap-16 lg:gap-24 max-w-7xl mx-auto">
|
||||
{/* Left Column - About */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-10%" }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1] }}
|
||||
className="space-y-8"
|
||||
>
|
||||
<div>
|
||||
<motion.div
|
||||
className="font-montserrat tracking-[0.3em] text-white/60 text-xs uppercase mb-4 sm:mb-6 font-semibold"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
About
|
||||
</motion.div>
|
||||
<motion.p
|
||||
className="font-montserrat text-white/80 text-base sm:text-lg leading-relaxed font-medium"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1], delay: 0.2 }}
|
||||
>
|
||||
With a versatile athletic background and a strong stage presence as a drummer, I bring dynamic and charismatic energy to every project. Reliable, adaptable, and passionate about delivering outstanding results.
|
||||
</motion.p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Right Column - Measurements */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-10%" }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1] }}
|
||||
className="space-y-10 sm:space-y-12"
|
||||
>
|
||||
<div>
|
||||
<motion.div
|
||||
className="font-montserrat tracking-[0.3em] text-white/60 text-xs uppercase mb-6 font-semibold"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
Measurements
|
||||
</motion.div>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-x-6 sm:gap-x-8 gap-y-6">
|
||||
<div>
|
||||
<h2 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Height</h2>
|
||||
<p className="font-montserrat text-lg sm:text-xl text-white font-medium">183 cm</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Chest</h2>
|
||||
<p className="font-montserrat text-lg sm:text-xl text-white font-medium">95 cm</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Waist</h2>
|
||||
<p className="font-montserrat text-lg sm:text-xl text-white font-medium">73 cm</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Hip</h2>
|
||||
<p className="font-montserrat text-lg sm:text-xl text-white font-medium">88 cm</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Suit Size</h2>
|
||||
<p className="font-montserrat text-lg sm:text-xl text-white font-medium">48</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Shoe Size</h2>
|
||||
<p className="font-montserrat text-lg sm:text-xl text-white font-medium">45</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-3 font-semibold">Features</h3>
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-2">
|
||||
<p className="font-montserrat text-white/80 text-sm sm:text-base font-medium">Brown Eyes</p>
|
||||
<p className="font-montserrat text-white/80 text-sm sm:text-base font-medium">Brown Hair</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Categories Section */}
|
||||
<section className="relative py-16 sm:py-20 md:py-24 lg:py-32 bg-white">
|
||||
<div className="absolute inset-0" style={{
|
||||
backgroundImage: `linear-gradient(to right, rgba(0, 0, 0, 0.03) 1px, transparent 1px), linear-gradient(to bottom, rgba(0, 0, 0, 0.03) 1px, transparent 1px)`,
|
||||
backgroundSize: '48px 48px'
|
||||
}} />
|
||||
<div className="container mx-auto px-4 sm:px-6 md:px-8 relative">
|
||||
<motion.div
|
||||
className="max-w-screen-lg mx-auto mb-16 sm:mb-20 md:mb-24 lg:mb-32 text-center space-y-4 sm:space-y-6"
|
||||
>
|
||||
<motion.div
|
||||
className="font-montserrat tracking-[0.3em] sm:tracking-[0.5em] text-black/70 text-xs sm:text-sm uppercase font-medium"
|
||||
>
|
||||
Categories
|
||||
</motion.div>
|
||||
<motion.h2
|
||||
className="font-playfair text-2xl sm:text-3xl md:text-4xl lg:text-5xl text-black font-bold"
|
||||
>
|
||||
Modeling Services
|
||||
</motion.h2>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-12 max-w-5xl mx-auto">
|
||||
{/* Commercial */}
|
||||
<motion.div
|
||||
className="text-center space-y-4"
|
||||
>
|
||||
<div className="w-16 h-16 mx-auto mb-6">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" className="w-full h-full text-black/70">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M3 3h18v18H3zM9 3v18M15 3v18M3 9h18M3 15h18" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="font-montserrat text-black uppercase tracking-[0.2em] text-base font-semibold">Commercial</h3>
|
||||
<p className="font-montserrat text-black/70 text-sm leading-relaxed max-w-xs mx-auto font-medium">
|
||||
Product campaigns, lifestyle shoots, and brand representation with a focus on authentic engagement and natural presence.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Fitness */}
|
||||
<motion.div
|
||||
className="text-center space-y-4"
|
||||
>
|
||||
<div className="w-16 h-16 mx-auto mb-6">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" className="w-full h-full text-black/70">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M6.5 6.5h11v11h-11z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M3 9V6.5h3.5M21 9V6.5h-3.5M3 15v2.5h3.5M21 15v2.5h-3.5" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="font-montserrat text-black uppercase tracking-[0.2em] text-base font-semibold">Fitness</h3>
|
||||
<p className="font-montserrat text-black/70 text-sm leading-relaxed max-w-xs mx-auto font-medium">
|
||||
Athletic wear, sports campaigns, and dynamic movement shoots showcasing strength and functional fitness.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Editorial */}
|
||||
<motion.div
|
||||
className="text-center space-y-4"
|
||||
>
|
||||
<div className="w-16 h-16 mx-auto mb-6">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" className="w-full h-full text-black/70">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5" d="M4 21V3h16v18M8 3v18M16 3v18" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="font-montserrat text-black uppercase tracking-[0.2em] text-base font-semibold">Editorial</h3>
|
||||
<p className="font-montserrat text-black/70 text-sm leading-relaxed max-w-xs mx-auto font-medium">
|
||||
Fashion editorials, magazine features, and artistic collaborations with a focus on storytelling and style.
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Portfolio Section */}
|
||||
<section id="portfolio" className="relative py-16 sm:py-20 md:py-24 lg:py-32 bg-white">
|
||||
<div className="absolute inset-0" style={{
|
||||
backgroundImage: `linear-gradient(to right, rgba(0, 0, 0, 0.03) 1px, transparent 1px), linear-gradient(to bottom, rgba(0, 0, 0, 0.03) 1px, transparent 1px)`,
|
||||
backgroundSize: '48px 48px'
|
||||
}} />
|
||||
<div className="container mx-auto px-4 sm:px-6 md:px-8 relative">
|
||||
<motion.div
|
||||
className="max-w-screen-lg mx-auto mb-16 sm:mb-20 md:mb-24 lg:mb-32 text-center space-y-4 sm:space-y-6"
|
||||
>
|
||||
<motion.div
|
||||
className="font-montserrat tracking-[0.3em] sm:tracking-[0.5em] text-black/70 text-xs sm:text-sm uppercase font-medium"
|
||||
>
|
||||
Portfolio
|
||||
</motion.div>
|
||||
<motion.h2
|
||||
className="font-playfair text-2xl sm:text-3xl md:text-4xl lg:text-5xl text-black font-bold"
|
||||
>
|
||||
Selected Work
|
||||
</motion.h2>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6 md:gap-8 lg:gap-12">
|
||||
{[1, 2, 3, 4, 5, 6].map((index) => (
|
||||
<MotionCard
|
||||
key={index}
|
||||
className="overflow-hidden group border-0 bg-transparent relative"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1], delay: index * 0.05 }}
|
||||
>
|
||||
<motion.div
|
||||
className="relative w-full aspect-[3/4] overflow-hidden"
|
||||
whileHover={{ scale: 1.02 }}
|
||||
transition={{ duration: 0.4, ease: [0.32, 0.72, 0, 1] }}
|
||||
>
|
||||
<Image
|
||||
src={`/images/portfolio-${index}.jpg`}
|
||||
alt={`Portfolio image ${index} - Professional modeling work by Johannes Behrens`}
|
||||
fill
|
||||
className="object-cover grayscale group-hover:grayscale-0 transition-all duration-200"
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||
quality={85}
|
||||
priority={index <= 2}
|
||||
loading={index <= 2 ? "eager" : "lazy"}
|
||||
/>
|
||||
<motion.div
|
||||
className="absolute inset-0 bg-gradient-to-b from-black/0 to-black/20 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
|
||||
/>
|
||||
</motion.div>
|
||||
</MotionCard>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact Section */}
|
||||
<section id="contact" className="py-16 sm:py-20 md:py-24 lg:py-32 bg-black">
|
||||
<div className="container mx-auto px-4 sm:px-6 md:px-8">
|
||||
<motion.div
|
||||
className="max-w-screen-lg mx-auto"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1] }}
|
||||
>
|
||||
<div className="text-center mb-12 sm:mb-16 md:mb-20 lg:mb-24">
|
||||
<motion.div
|
||||
className="font-montserrat tracking-[0.3em] sm:tracking-[0.4em] md:tracking-[0.5em] text-white/60 text-xs sm:text-sm uppercase mb-4 font-semibold"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
Contact
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
className="grid grid-cols-1 md:grid-cols-2 gap-12 sm:gap-16 md:gap-24 max-w-5xl mx-auto text-center"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1], delay: 0.2 }}
|
||||
>
|
||||
<div className="space-y-12">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1] }}
|
||||
>
|
||||
<h3 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Email</h3>
|
||||
<motion.p
|
||||
className="font-montserrat text-lg sm:text-xl md:text-2xl lg:text-3xl break-words font-medium"
|
||||
whileHover={{ opacity: 0.8 }}
|
||||
transition={{ duration: 0.4, ease: [0.32, 0.72, 0, 1] }}
|
||||
>
|
||||
<Link
|
||||
href="mailto:contact@johannesbehrens.com"
|
||||
className={`hover:opacity-80 transition-opacity ${focusStyles}`}
|
||||
tabIndex={0}
|
||||
>
|
||||
contact@johannesbehrens.com
|
||||
</Link>
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1], delay: 0.1 }}
|
||||
>
|
||||
<h3 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Based in</h3>
|
||||
<p className="font-montserrat text-lg sm:text-xl md:text-2xl lg:text-3xl font-medium">Hamburg, Germany</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
<div className="space-y-12">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1], delay: 0.2 }}
|
||||
>
|
||||
<h3 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Agency</h3>
|
||||
<p className="font-montserrat text-lg sm:text-xl md:text-2xl lg:text-3xl font-medium">Model Management</p>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, ease: [0.32, 0.72, 0, 1], delay: 0.3 }}
|
||||
>
|
||||
<h3 className="font-montserrat text-white/60 uppercase tracking-[0.2em] text-[10px] sm:text-xs mb-2 font-semibold">Social</h3>
|
||||
<div className="space-y-3">
|
||||
<motion.p
|
||||
className="font-montserrat text-lg sm:text-xl md:text-2xl lg:text-3xl font-medium"
|
||||
whileHover={{ opacity: 0.8 }}
|
||||
transition={{ duration: 0.4, ease: [0.32, 0.72, 0, 1] }}
|
||||
>
|
||||
<Link
|
||||
href="https://www.instagram.com/johannes.brh/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`hover:opacity-80 transition-opacity duration-200 ${focusStyles}`}
|
||||
tabIndex={0}
|
||||
>
|
||||
Instagram
|
||||
</Link>
|
||||
</motion.p>
|
||||
<motion.p
|
||||
className="font-montserrat text-lg sm:text-xl md:text-2xl lg:text-3xl font-medium"
|
||||
whileHover={{ opacity: 0.8 }}
|
||||
transition={{ duration: 0.4, ease: [0.32, 0.72, 0, 1] }}
|
||||
>
|
||||
<Link href="#" className="hover:opacity-80 transition-opacity duration-200">
|
||||
LinkedIn
|
||||
</Link>
|
||||
</motion.p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<footer
|
||||
className="py-8 bg-black border-t border-white/10"
|
||||
role="contentinfo"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<p className="font-montserrat text-sm text-white/90">
|
||||
Made with ❤️ by{" "}
|
||||
<Link
|
||||
href="https://jleibl.net"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`text-white hover:text-white/80 transition-colors duration-200 ${focusStyles}`}
|
||||
aria-label="Visit Jan-Marlon Leibl's website (opens in new tab)"
|
||||
>
|
||||
Jan-Marlon Leibl
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</motion.main>
|
||||
</>
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
@ -1,21 +1,48 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap');
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
@layer base {
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
body {
|
||||
@apply bg-black text-white;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
@layer utilities {
|
||||
.vertical-text {
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: mixed;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: 0 0% 0%;
|
||||
--foreground: 0 0% 100%;
|
||||
--card: 0 0% 0%;
|
||||
--card-foreground: 0 0% 100%;
|
||||
--popover: 0 0% 0%;
|
||||
--popover-foreground: 0 0% 100%;
|
||||
--primary: 0 0% 100%;
|
||||
--primary-foreground: 0 0% 0%;
|
||||
--secondary: 0 0% 5%;
|
||||
--secondary-foreground: 0 0% 100%;
|
||||
--muted: 0 0% 10%;
|
||||
--muted-foreground: 0 0% 70%;
|
||||
--accent: 0 0% 100%;
|
||||
--accent-foreground: 0 0% 0%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 20%;
|
||||
--input: 0 0% 20%;
|
||||
--ring: 0 0% 100%;
|
||||
--radius: 0;
|
||||
}
|
||||
|
@ -1,18 +1,67 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
import animate from "tailwindcss-animate";
|
||||
|
||||
export default {
|
||||
content: [
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
background: "var(--background)",
|
||||
foreground: "var(--foreground)",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))'
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-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))',
|
||||
foreground: 'hsl(var(--destructive-foreground))'
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
chart: {
|
||||
'1': 'hsl(var(--chart-1))',
|
||||
'2': 'hsl(var(--chart-2))',
|
||||
'3': 'hsl(var(--chart-3))',
|
||||
'4': 'hsl(var(--chart-4))',
|
||||
'5': 'hsl(var(--chart-5))'
|
||||
}
|
||||
},
|
||||
fontFamily: {
|
||||
prompt:['Prompt', 'sans-serif'],
|
||||
playfair: ['Playfair Display', 'serif'],
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [],
|
||||
plugins: [animate],
|
||||
} satisfies Config;
|
||||
|