Almost done

This commit is contained in:
2026-03-05 17:35:27 +01:00 Unverified
parent 278471e53b
commit e44fceaf32
30 changed files with 1077 additions and 572 deletions
+31
View File
@@ -0,0 +1,31 @@
// app/components/BeachPromo.tsx
import Link from 'next/link';
import { FaVolleyballBall } from "react-icons/fa";
export default function BeachPromo() {
return (
<section className="bg-[#fdb84b] flex justify-center py-4 transition-all duration-500 hover:brightness-105 group/promo cursor-pointer">
<div className="p-2.5">
<h2 className="font-beachday text-black text-center text-[clamp(1.2rem,6vw,2.25rem)] uppercase tracking-tight flex items-baseline justify-center flex-wrap gap-x-3 md:gap-x-5">
<span className="whitespace-nowrap transition-all duration-300 group-hover/promo:opacity-80">
Kolla in vår
</span>
<Link
href="/beach"
className="relative text-[#406185] transition-all duration-300 inline-flex items-center gap-3 group/link hover:tracking-wider hover:-translate-y-1"
>
<span className="relative">
Beachturnering
<span className="absolute -bottom-1 left-0 w-full h-1 bg-[#406185] transform scale-x-0 transition-transform duration-300 origin-left group-hover/link:scale-x-100" />
</span>
<FaVolleyballBall
className="text-[#406185] transition-all duration-700 ease-out group-hover/promo:rotate-360 group-hover/link:scale-125 group-hover/link:text-black self-center"
size="0.8em"
/>
</Link>
</h2>
</div>
</section>
);
}
+15
View File
@@ -0,0 +1,15 @@
// app/components/Footer.tsx
export default function Footer() {
return (
<footer className="bg-black text-center py-4 border-t border-white/10">
<div className="group inline-block cursor-default">
<p className="font-bold text-gray-400 transition-colors duration-300 group-hover:text-white">
LVS - Lerbergets Volleybollsällskap {new Date().getFullYear()}
<span className="inline-block ml-1 transition-transform duration-500 group-hover:rotate-360">®</span>
</p>
{/* En subtil linje som växer fram under footern när man hovrar */}
<div className="h-0.5 w-0 bg-[#f1c50e] mx-auto transition-all duration-500 group-hover:w-full mt-1" />
</div>
</footer>
);
}
+18
View File
@@ -0,0 +1,18 @@
// app/components/Header.tsx
import Link from 'next/link';
export default function Header() {
return (
<header className="bg-black flex justify-center sticky top-0 z-40 shadow-lg">
<div className="w-85 mx-auto text-center">
<Link href="/" className="inline-block group">
<img
src="/images/logo.svg"
alt="Lerbergets Volleybollsällskap"
className="max-h-32 min-h-16 my-4 md:max-h-52 md:min-h-24 md:my-6 mx-auto block transition-all duration-500 ease-out group-hover:scale-105 group-active:scale-95"
/>
</Link>
</div>
</header>
);
}
+49
View File
@@ -0,0 +1,49 @@
// app/components/HeroButton.tsx
import Link from 'next/link';
import React from 'react';
interface HeroButtonProps {
href?: string;
onClick?: () => void;
children: React.ReactNode;
className?: string;
}
export default function HeroButton({ href, onClick, children, className = "" }: HeroButtonProps) {
const baseStyles = `
text-[#406185] bg-white border-none py-3.5 px-7.5
text-center font-bold uppercase w-fit text-[1.1rem]
transition-all duration-300 hover:scale-105 active:scale-95
shadow-md hover:shadow-lg cursor-pointer inline-block
${className}
`;
if (onClick) {
return (
<button onClick={onClick} className={baseStyles}>
{children}
</button>
);
}
if (href) {
const isExternal = href.startsWith('http');
if (isExternal) {
return (
<a href={href} target="_blank" rel="noreferrer" className={baseStyles}>
{children}
</a>
);
}
return (
<Link href={href} className={baseStyles}>
{children}
</Link>
);
}
return null;
}
+114
View File
@@ -0,0 +1,114 @@
// app/components/HomeClientContent.tsx
'use client';
import { useState } from 'react';
import HeroButton from './HeroButton';
import InfoCard from './InfoCard';
import SocialIcon from './SocialIcon';
import MemberModal from './Modals/Member';
import SubscribeModal from './Modals/Subscribe';
import { FaFacebookSquare, FaInstagram } from "react-icons/fa";
import { IoIosMail } from "react-icons/io";
export default function HomeClientContent() {
const [activeModal, setActiveModal] = useState<'member' | 'subscribe' | null>(null);
const closeModals = () => setActiveModal(null);
return (
<>
{/* Hero / Info Section */}
<section className="bg-[#406185] flex justify-center text-white">
<div className="flex flex-col md:flex-row max-w-300 my-12.5 mx-5 w-full">
<div className="flex flex-col justify-center flex-1">
<div className="w-fit">
<h1 className="uppercase text-[2.5rem] font-bold leading-[1.1] mb-2.5">LVS</h1>
<div className="bg-white w-full h-1.5"></div>
</div>
<h4 className="italic text-xl my-2.5 font-bold">Lerbergets Volleybollsällskap</h4>
<p className="max-w-200 leading-[1.1]">
Klubben startade den 1 mars 2018 och har i snabb takt utvecklats...
</p>
<div className="flex flex-col gap-2.5 mt-10">
<HeroButton onClick={() => setActiveModal('member')}>Bli Medlem</HeroButton>
<HeroButton href="#traningstider">Träningstider</HeroButton>
<HeroButton href="https://www.basesport.se/category/lerbergets-vbk">Klubbkollektion</HeroButton>
</div>
</div>
<div className="hidden md:flex items-center p-2.5 justify-end">
<img src="/images/player.webp" alt="Spelare" className="max-h-112.5 player-animation" />
</div>
</div>
</section>
{/* Träningstider Section */}
<section id="traningstider" className="bg-white flex justify-center">
<div className="flex flex-col items-center w-full my-7.5 gap-2">
<InfoCard
title="Träningstider"
description="Här hittar du alltid uppdaterade träningstider från Laget.se"
actionLabel="Se nästa träningstid"
href="https://www.laget.se/LVS/Event/Month"
/>
<InfoCard
title="Prenumerera på träningstider"
description="Våra träningstider finns som en kalender man kan prenumerera på"
actionLabel="Prenumerera"
onClick={() => setActiveModal('subscribe')}
/>
</div>
</section>
<section className="bg-[#406185] flex justify-center text-white">
<div className="flex flex-wrap-reverse justify-center gap-12 w-full max-w-300 my-16 px-5">
{/* Sociala Medier Ikoner */}
<div className="flex flex-wrap justify-center items-center gap-10">
<SocialIcon title="Instagram" href="https://www.instagram.com/lerbergets_volleyboll/">
<FaInstagram className="h-20 w-20" />
</SocialIcon>
<SocialIcon title="Facebook" href="https://www.facebook.com/lerbergetsvolleyboll">
<FaFacebookSquare className="h-20 w-20" />
</SocialIcon>
<SocialIcon title="Laget.se" href="https://www.laget.se/LVS">
<img src="/images/laget.se.svg" alt="Laget.se" className="h-18 w-18" />
</SocialIcon>
</div>
{/* Kontaktinformation */}
<div className="text-center p-2.5 flex flex-col justify-center items-center">
<h3 className="uppercase font-bold text-[1.875rem] leading-[1.2] mb-4">Kontakta oss</h3>
<p className="text-[1rem] leading-[1.6] opacity-90">Vi finns Instagram, Facebook och laget.se</p>
{/* Klickbar Adress - Länkar till Google Maps */}
<a
target="_blank"
rel="noreferrer"
href="https://maps.app.goo.gl/W81bT9X44Yfxsnoh7"
className="text-[1rem] leading-[1.6] my-2 hover:text-[#f1c50e] transition-colors duration-300"
>
Höganäs Sportcenter, Friluftsvägen 10, 263 54 Lerberget
</a>
{/* Levande E-postlänk med brand-gul färg */}
<a
href="mailto:info@lerbergetsvolleyboll.se?subject=Kontakta%20oss"
className="group flex justify-center items-center text-[#f1c50e] font-bold text-[1.1rem] mt-2 relative pb-1"
>
<IoIosMail className="h-7 w-7 mr-2" size={24} />
<span>info@lerbergetsvolleyboll.se</span>
{/* Underlinje som expanderar vid hovring */}
<span className="absolute bottom-0 left-0 w-full h-0.5 bg-[#f1c50e] transform scale-x-0 transition-transform duration-300 origin-left group-hover:scale-x-100" />
</a>
</div>
</div>
</section>
{activeModal === 'member' && <MemberModal onClose={closeModals} />}
{activeModal === 'subscribe' && <SubscribeModal onClose={closeModals} />}
</>
);
}
+61
View File
@@ -0,0 +1,61 @@
// app/components/InfoCard.tsx
import { HiOutlineArrowNarrowRight } from "react-icons/hi";
interface InfoCardProps {
title: string;
description: string;
actionLabel: string;
href?: string;
onClick?: () => void;
}
export default function InfoCard({ title, description, actionLabel, href, onClick }: InfoCardProps) {
const actionClasses = `
group inline-flex items-center gap-2
text-[#f1c50e] font-bold uppercase text-[1rem]
tracking-wider transition-all duration-300
relative pb-1
`;
const content = (
<>
<span>{actionLabel}</span>
<HiOutlineArrowNarrowRight
className="text-[1.2rem] transition-transform duration-300 group-hover:translate-x-2"
/>
<span className="absolute bottom-0 left-0 w-full h-0.5 bg-[#f1c50e] transform scale-x-0 transition-transform duration-300 origin-left group-hover:scale-x-100" />
</>
);
return (
<div className="bg-[#406185] text-white w-full max-w-180 py-8 px-6 text-center shadow-lg rounded-sm">
<h2 className="uppercase text-[clamp(1.5rem,8vw,2.25rem)] font-bold leading-tight mb-2">
{title}
</h2>
<p className="mb-6 text-[1rem] leading-[1.4] opacity-90">
{description}
</p>
<div className="flex justify-center">
{href ? (
<a
href={href}
target="_blank"
rel="noreferrer"
className={actionClasses}
>
{content}
</a>
) : (
<button
onClick={onClick}
className={actionClasses}
>
{content}
</button>
)}
</div>
</div>
);
}
+19
View File
@@ -0,0 +1,19 @@
// app/components/Modals/Member.tsx
import { ModalProps } from "@/app/types";
import Modal from "./Modal";
import ModalAction from "./ModalAction";
export default function MemberModal({ onClose }: ModalProps) {
return (
<Modal onClose={onClose}>
<img src="/images/logo-black.svg" alt="Logo" className="h-25" />
<h2 className="text-[#406185] my-2.5 text-2xl font-bold uppercase">Registrera dig</h2>
<p className="text-[#406185] my-2.5">Trycka knappen nedan för att starta registreringen. Du kommer att skickas till laget.se</p>
<ModalAction href="https://www.laget.se/LVS/Member" className="mt-5">
Bli medlem
</ModalAction>
</Modal>
);
}
+38
View File
@@ -0,0 +1,38 @@
// app/components/Modals/Modal.tsx
import { ModalProps } from "@/app/types";
import React from "react";
interface BaseModalProps extends ModalProps {
onClose: () => void;
children: React.ReactNode;
}
export default function Modal({ onClose, children }: BaseModalProps) {
return (
<div
className="fixed inset-0 z-50 bg-black/60 flex justify-center items-center overflow-auto"
onClick={onClose}
>
<div
className="bg-white p-5 w-[80vw] max-w-max min-w-75 rounded-[15px] relative flex flex-col items-center text-center font-semibold text-[#406185]"
onClick={(e) => e.stopPropagation()}
>
<button
className="absolute right-3 top-3 p-2 rounded-full transition-all duration-300 ease-in-out hover:bg-gray-100 hover:rotate-90 active:scale-90 group cursor-pointer"
onClick={onClose}
aria-label="Stäng"
>
<svg
viewBox="0 0 413.348 413.348"
className="w-4 h-4 fill-black transition-colors duration-300 group-hover:fill-[#406185]"
>
<path d="m413.348 24.354-24.354-24.354-182.32 182.32-182.32-182.32-24.354 24.354 182.32 182.32-182.32 182.32 24.354 24.354 182.32-182.32 182.32 182.32 24.354-24.354-182.32-182.32z" />
</svg>
</button>
{children}
</div>
</div>
);
}
+27
View File
@@ -0,0 +1,27 @@
// app/components/Modals/ModalAction.tsx
import React from 'react';
interface ModalActionProps {
href: string;
children: React.ReactNode;
className?: string; // To allow for specific margins like mt-5
}
export default function ModalAction({ href, children, className = "" }: ModalActionProps) {
return (
<a
href={href}
target={href.startsWith('http') ? "_blank" : undefined}
rel={href.startsWith('http') ? "noreferrer" : undefined}
className={`
text-white bg-[#406185] uppercase font-bold py-2.25 px-11.5
rounded-[50px] text-[1rem] text-center transition-all duration-300
hover:opacity-90 hover:scale-105 active:scale-95 shadow-md
hover:shadow-lg inline-block ${className}
`}
>
{children}
</a>
);
}
+22
View File
@@ -0,0 +1,22 @@
// app/components/Modals/Subscribe.tsx
import { ModalProps } from "@/app/types";
import Modal from "./Modal";
import ModalAction from "./ModalAction";
export default function SubscribeModal({ onClose }: ModalProps) {
return (
<Modal onClose={onClose}>
<h2 className="text-[#406185] my-2.5 text-2xl font-bold uppercase">Prenumerera</h2>
<p className="text-[#406185] my-2.5">Välj vilken LEVEL du vill prenumerera .</p>
<div className="flex flex-row flex-wrap justify-center gap-6.25 mt-5 max-w-162.5">
<ModalAction href="webcal://cal.laget.se/lvs-level-5-6.ics">Level 5-6</ModalAction>
<ModalAction href="webcal://cal.laget.se/lvs-level-7-8.ics">Level 7-8</ModalAction>
<ModalAction href="webcal://cal.laget.se/lvs-level-8-9.ics">Level 8-9</ModalAction>
<ModalAction href="webcal://cal.laget.se/lvs-level-10.ics">Level 10</ModalAction>
<ModalAction href="webcal://cal.laget.se/LVS.ics">Hela klubben</ModalAction>
</div>
</Modal>
);
}
+23
View File
@@ -0,0 +1,23 @@
// app/components/SocialIcon.tsx
import React from 'react';
interface SocialIconProps {
href: string;
title: string;
children: React.ReactNode;
}
export default function SocialIcon({ href, title, children }: SocialIconProps) {
return (
<a
target="_blank"
rel="noreferrer"
title={title}
href={href}
className="transition-all duration-300 hover:scale-110 active:scale-90 hover:brightness-110"
>
{children}
</a>
);
}