Almost done
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 på 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} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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 på 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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å.</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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user