Initial commit: Family business website with summer vibes

This commit is contained in:
boilerrat 2025-03-08 22:40:24 -05:00
commit d01562cf03
17 changed files with 3245 additions and 0 deletions

36
.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# large files
/public/images/

79
README.md Normal file
View File

@ -0,0 +1,79 @@
# Family Business Website
A beautiful, responsive website for a family business with summer sunset vibes. This project is built with Next.js, React, TypeScript, Tailwind CSS, and Framer Motion.
## Features
- Modern, responsive design with summer sunset color palette
- Smooth animations and transitions with Framer Motion
- Fully responsive for all device sizes
- Contact form with validation
- Optimized images and performance
## Tech Stack
- **Next.js**: React framework for production
- **React**: JavaScript library for building user interfaces
- **TypeScript**: Typed JavaScript
- **Tailwind CSS**: Utility-first CSS framework
- **Framer Motion**: Animation library for React
## Getting Started
### Prerequisites
- Node.js 14.0 or later
- npm or yarn
### Installation
1. Clone the repository
```bash
git clone https://github.com/yourusername/family-business.git
cd family-business
```
2. Install dependencies
```bash
npm install
# or
yarn install
```
3. Run the development server
```bash
npm run dev
# or
yarn dev
```
4. Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Project Structure
- `src/app`: Next.js App Router pages
- `src/components`: Reusable React components
- `public`: Static assets
- `tailwind.config.js`: Tailwind CSS configuration
## Color Palette
The website uses a summer sunset-inspired color palette:
- Sunset Orange: `#FF7B54`
- Sunset Pink: `#FFB26B`
- Sunset Yellow: `#FFD56F`
- Sunset Purple: `#939B62`
- Evening Blue: `#1A5F7A`
- Night Blue: `#002B5B`
- Warm White: `#FFF8EA`
- Sand: `#E1D7C6`
## License
This project is licensed under the MIT License - see the LICENSE file for details.
## Acknowledgments
- Design inspiration from [Dribbble](https://dribbble.com/shots/25718593-Coolest-Meme-Coin-Website)
- Images from [PlaceKitten](https://placekitten.com/)

14
next.config.js Normal file
View File

@ -0,0 +1,14 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'placekitten.com',
},
],
},
};
module.exports = nextConfig;

1741
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

36
package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "family-buisness",
"version": "1.0.0",
"private": true,
"main": "index.js",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@radix-ui/react-slot": "^1.1.2",
"@tailwindcss/postcss": "^4.0.12",
"@types/node": "^22.13.10",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"autoprefixer": "^10.4.20",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.4.10",
"lucide-react": "^0.479.0",
"next": "^15.2.1",
"postcss": "^8.5.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.12",
"typescript": "^5.8.2"
}
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
'@tailwindcss/postcss': {},
autoprefixer: {},
},
}

99
src/app/globals.css Normal file
View File

@ -0,0 +1,99 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 255, 248, 234; /* warm-white */
--background-end-rgb: 225, 215, 198; /* sand */
/* Custom colors */
--sunset-orange: #FF7B54;
--sunset-pink: #FFB26B;
--sunset-yellow: #FFD56F;
--sunset-purple: #939B62;
--evening-blue: #1A5F7A;
--night-blue: #002B5B;
--warm-white: #FFF8EA;
--sand: #E1D7C6;
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
@layer base {
h1, h2, h3, h4, h5, h6 {
font-weight: 700;
}
p, span, a, button {
font-weight: 400;
}
}
@layer components {
.btn-primary {
background-color: var(--sunset-orange);
color: var(--warm-white);
padding: 0.75rem 1.5rem;
border-radius: 9999px;
transition: background-color 0.3s ease;
}
.btn-primary:hover {
background-color: var(--sunset-pink);
}
.btn-secondary {
background-color: var(--evening-blue);
color: var(--warm-white);
padding: 0.75rem 1.5rem;
border-radius: 9999px;
transition: background-color 0.3s ease;
}
.btn-secondary:hover {
background-color: var(--night-blue);
}
.section {
padding-top: 4rem;
padding-bottom: 4rem;
}
@media (min-width: 768px) {
.section {
padding-top: 6rem;
padding-bottom: 6rem;
}
}
.container-custom {
max-width: 80rem;
margin-left: auto;
margin-right: auto;
padding-left: 1rem;
padding-right: 1rem;
}
@media (min-width: 640px) {
.container-custom {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
}
@media (min-width: 1024px) {
.container-custom {
padding-left: 2rem;
padding-right: 2rem;
}
}
}

27
src/app/layout.tsx Normal file
View File

@ -0,0 +1,27 @@
import './globals.css';
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
});
export const metadata: Metadata = {
title: 'Family Business - Summer Vibes',
description: 'A family business with summer sunset vibes',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
{children}
</body>
</html>
);
}

21
src/app/page.tsx Normal file
View File

@ -0,0 +1,21 @@
import Image from 'next/image';
import Link from 'next/link';
import { Navbar } from '@/components/navbar';
import { Footer } from '@/components/footer';
import { HeroSection } from '@/components/hero-section';
import { FeaturesSection } from '@/components/features-section';
import { AboutSection } from '@/components/about-section';
import { ContactSection } from '@/components/contact-section';
export default function Home() {
return (
<main className="flex min-h-screen flex-col">
<Navbar />
<HeroSection />
<FeaturesSection />
<AboutSection />
<ContactSection />
<Footer />
</main>
);
}

View File

@ -0,0 +1,119 @@
'use client';
import { motion } from 'framer-motion';
import Image from 'next/image';
import { CheckCircle } from 'lucide-react';
export function AboutSection() {
const benefits = [
'Family-owned and operated since 2005',
'Dedicated to exceptional customer service',
'Creating summer vibes all year round',
'Supporting our local community',
'Environmentally conscious practices',
'Satisfaction guaranteed on all services',
];
return (
<section id="about" className="section bg-warm-white">
<div className="container-custom">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
{/* Image Side */}
<motion.div
initial={{ opacity: 0, x: -50 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
className="relative"
>
<div className="relative h-[600px] rounded-2xl overflow-hidden shadow-xl">
<Image
src="/images/about.jpg"
alt="Our Family Business"
fill
className="object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-night-blue/50 to-transparent"></div>
</div>
{/* Floating Card */}
<div className="absolute -bottom-8 -right-8 bg-warm-white p-6 rounded-xl shadow-lg max-w-xs">
<div className="flex items-center space-x-4 mb-4">
<div className="w-12 h-12 bg-sunset-orange rounded-full flex items-center justify-center">
<span className="text-warm-white text-xl" style={{ fontWeight: 700 }}>18</span>
</div>
<div>
<h4 className="text-night-blue" style={{ fontWeight: 700 }}>Years of Experience</h4>
<p className="text-sm text-evening-blue/80">Serving our community</p>
</div>
</div>
<div className="h-1 w-full bg-sand mb-4"></div>
<p className="text-evening-blue italic">
"We started this business with a simple mission: bring the joy of summer to families all year round."
</p>
</div>
{/* Decorative Elements */}
<div className="absolute -top-8 -left-8 w-24 h-24 bg-sunset-yellow/20 rounded-full blur-xl"></div>
<div className="absolute top-1/2 -left-4 w-8 h-8 bg-sunset-orange rounded-full"></div>
</motion.div>
{/* Content Side */}
<motion.div
initial={{ opacity: 0, x: 50 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
className="space-y-6"
>
<span className="inline-block px-4 py-2 bg-sunset-orange/10 text-sunset-orange rounded-full" style={{ fontWeight: 500 }}>
About Us
</span>
<h2 className="text-3xl md:text-4xl text-night-blue" style={{ fontWeight: 700 }}>
A Family Business With <span className="text-sunset-orange">Summer at Heart</span>
</h2>
<p className="text-evening-blue/80">
Founded in 2005, our family business has been bringing the warm, relaxed feeling of summer to our community for over 18 years. What started as a small operation has grown into a beloved local institution, but our core values remain the same.
</p>
<p className="text-evening-blue/80">
We believe in creating experiences that make people feel like they're enjoying a perfect summer evening - relaxed, happy, and surrounded by those they love. Our team, many of whom are family members, are dedicated to providing exceptional service with that special summer touch.
</p>
<div className="pt-4 grid grid-cols-1 md:grid-cols-2 gap-4">
{benefits.map((benefit, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
viewport={{ once: true }}
className="flex items-start space-x-3"
>
<CheckCircle className="h-6 w-6 text-sunset-orange flex-shrink-0 mt-0.5" />
<span className="text-evening-blue">{benefit}</span>
</motion.div>
))}
</div>
<div className="pt-6">
<div className="flex items-center space-x-4">
<div className="relative w-16 h-16 rounded-full overflow-hidden">
<Image
src="/images/founder.jpg"
alt="Founder"
fill
className="object-cover"
/>
</div>
<div>
<h4 className="text-night-blue" style={{ fontWeight: 700 }}>Jane & John Smith</h4>
<p className="text-evening-blue/80">Founders</p>
</div>
</div>
</div>
</motion.div>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,441 @@
'use client';
import { useState } from 'react';
import { motion } from 'framer-motion';
import { Mail, Phone, MapPin, Send } from 'lucide-react';
export function ContactSection() {
const [formState, setFormState] = useState({
name: '',
email: '',
phone: '',
message: '',
});
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSubmitted, setIsSubmitted] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormState((prev) => ({ ...prev, [name]: value }));
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
// Simulate form submission
setTimeout(() => {
setIsSubmitting(false);
setIsSubmitted(true);
setFormState({
name: '',
email: '',
phone: '',
message: '',
});
// Reset success message after 5 seconds
setTimeout(() => {
setIsSubmitted(false);
}, 5000);
}, 1500);
};
return (
<section id="contact" className="section" style={{
background: 'linear-gradient(to bottom, rgba(225, 215, 198, 0.3), var(--warm-white))'
}}>
<div className="container-custom">
<div style={{ textAlign: 'center', maxWidth: '48rem', margin: '0 auto', marginBottom: '4rem' }}>
<motion.span
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
transition={{ duration: 0.5 }}
viewport={{ once: true }}
style={{
display: 'inline-block',
padding: '0.5rem 1rem',
backgroundColor: 'rgba(255, 123, 84, 0.1)',
color: 'var(--sunset-orange)',
borderRadius: '9999px',
fontWeight: 500,
marginBottom: '1rem'
}}
>
Get In Touch
</motion.span>
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.1 }}
viewport={{ once: true }}
style={{
fontSize: 'clamp(1.875rem, 3vw, 2.25rem)',
fontWeight: 700,
color: 'var(--night-blue)',
marginBottom: '1rem'
}}
>
Contact Us Today
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
viewport={{ once: true }}
style={{ color: 'rgba(26, 95, 122, 0.8)' }}
>
Have questions or ready to experience our summer vibes? Reach out to us and we'll get back to you as soon as possible.
</motion.p>
</div>
<div style={{
display: 'grid',
gridTemplateColumns: '1fr',
gap: '3rem'
}} className="lg:grid-cols-2">
{/* Contact Info */}
<motion.div
initial={{ opacity: 0, x: -50 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}
>
<div style={{
backgroundColor: 'var(--warm-white)',
padding: '2rem',
borderRadius: '0.75rem',
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
border: '1px solid var(--sand)'
}}>
<h3 style={{
fontSize: '1.5rem',
fontWeight: 700,
color: 'var(--night-blue)',
marginBottom: '1.5rem'
}}>Contact Information</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '1rem' }}>
<div style={{
width: '2.5rem',
height: '2.5rem',
backgroundColor: 'rgba(255, 123, 84, 0.1)',
borderRadius: '9999px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0
}}>
<Phone style={{ height: '1.25rem', width: '1.25rem', color: 'var(--sunset-orange)' }} />
</div>
<div>
<h4 style={{ fontWeight: 500, color: 'var(--night-blue)' }}>Phone</h4>
<p style={{ color: 'var(--evening-blue)' }}>(555) 123-4567</p>
</div>
</div>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '1rem' }}>
<div style={{
width: '2.5rem',
height: '2.5rem',
backgroundColor: 'rgba(255, 123, 84, 0.1)',
borderRadius: '9999px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0
}}>
<Mail style={{ height: '1.25rem', width: '1.25rem', color: 'var(--sunset-orange)' }} />
</div>
<div>
<h4 style={{ fontWeight: 500, color: 'var(--night-blue)' }}>Email</h4>
<p style={{ color: 'var(--evening-blue)' }}>hello@familybusiness.com</p>
</div>
</div>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '1rem' }}>
<div style={{
width: '2.5rem',
height: '2.5rem',
backgroundColor: 'rgba(255, 123, 84, 0.1)',
borderRadius: '9999px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0
}}>
<MapPin style={{ height: '1.25rem', width: '1.25rem', color: 'var(--sunset-orange)' }} />
</div>
<div>
<h4 style={{ fontWeight: 500, color: 'var(--night-blue)' }}>Address</h4>
<p style={{ color: 'var(--evening-blue)' }}>123 Summer Lane, Beachside, CA 90210</p>
</div>
</div>
</div>
<div style={{
marginTop: '2rem',
paddingTop: '2rem',
borderTop: '1px solid var(--sand)'
}}>
<h4 style={{ fontWeight: 500, color: 'var(--night-blue)', marginBottom: '1rem' }}>Business Hours</h4>
<div style={{
display: 'grid',
gridTemplateColumns: '1fr',
gap: '0.5rem'
}} className="sm:grid-cols-2">
<div>
<p style={{ fontWeight: 500, color: 'var(--evening-blue)' }}>Monday - Friday</p>
<p style={{ color: 'rgba(26, 95, 122, 0.8)' }}>9:00 AM - 6:00 PM</p>
</div>
<div>
<p style={{ fontWeight: 500, color: 'var(--evening-blue)' }}>Saturday</p>
<p style={{ color: 'rgba(26, 95, 122, 0.8)' }}>10:00 AM - 4:00 PM</p>
</div>
<div>
<p style={{ fontWeight: 500, color: 'var(--evening-blue)' }}>Sunday</p>
<p style={{ color: 'rgba(26, 95, 122, 0.8)' }}>Closed</p>
</div>
</div>
</div>
</div>
</motion.div>
{/* Contact Form */}
<motion.div
initial={{ opacity: 0, x: 50 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
>
<div style={{
backgroundColor: 'var(--warm-white)',
padding: '2rem',
borderRadius: '0.75rem',
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
border: '1px solid var(--sand)'
}}>
<h3 style={{
fontSize: '1.5rem',
fontWeight: 700,
color: 'var(--night-blue)',
marginBottom: '1.5rem'
}}>Send Us a Message</h3>
{isSubmitted ? (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
style={{
backgroundColor: 'rgba(255, 123, 84, 0.1)',
padding: '1.5rem',
borderRadius: '0.5rem',
textAlign: 'center'
}}
>
<div style={{
width: '4rem',
height: '4rem',
backgroundColor: 'var(--sunset-orange)',
borderRadius: '9999px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: '0 auto',
marginBottom: '1rem'
}}>
<Send style={{ height: '2rem', width: '2rem', color: 'var(--warm-white)' }} />
</div>
<h4 style={{
fontSize: '1.25rem',
fontWeight: 700,
color: 'var(--night-blue)',
marginBottom: '0.5rem'
}}>Message Sent!</h4>
<p style={{ color: 'var(--evening-blue)' }}>
Thank you for reaching out. We'll get back to you as soon as possible.
</p>
</motion.div>
) : (
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
<div>
<label
htmlFor="name"
style={{
display: 'block',
fontSize: '0.875rem',
fontWeight: 500,
color: 'var(--night-blue)',
marginBottom: '0.25rem'
}}
>
Your Name
</label>
<input
type="text"
id="name"
name="name"
value={formState.name}
onChange={handleChange}
required
style={{
width: '100%',
padding: '0.75rem 1rem',
borderRadius: '0.5rem',
border: '1px solid var(--sand)',
outline: 'none',
backgroundColor: 'var(--warm-white)'
}}
placeholder="John Smith"
/>
</div>
<div style={{
display: 'grid',
gridTemplateColumns: '1fr',
gap: '1.5rem'
}} className="md:grid-cols-2">
<div>
<label
htmlFor="email"
style={{
display: 'block',
fontSize: '0.875rem',
fontWeight: 500,
color: 'var(--night-blue)',
marginBottom: '0.25rem'
}}
>
Email Address
</label>
<input
type="email"
id="email"
name="email"
value={formState.email}
onChange={handleChange}
required
style={{
width: '100%',
padding: '0.75rem 1rem',
borderRadius: '0.5rem',
border: '1px solid var(--sand)',
outline: 'none',
backgroundColor: 'var(--warm-white)'
}}
placeholder="john@example.com"
/>
</div>
<div>
<label
htmlFor="phone"
style={{
display: 'block',
fontSize: '0.875rem',
fontWeight: 500,
color: 'var(--night-blue)',
marginBottom: '0.25rem'
}}
>
Phone Number
</label>
<input
type="tel"
id="phone"
name="phone"
value={formState.phone}
onChange={handleChange}
style={{
width: '100%',
padding: '0.75rem 1rem',
borderRadius: '0.5rem',
border: '1px solid var(--sand)',
outline: 'none',
backgroundColor: 'var(--warm-white)'
}}
placeholder="(555) 123-4567"
/>
</div>
</div>
<div>
<label
htmlFor="message"
style={{
display: 'block',
fontSize: '0.875rem',
fontWeight: 500,
color: 'var(--night-blue)',
marginBottom: '0.25rem'
}}
>
Your Message
</label>
<textarea
id="message"
name="message"
value={formState.message}
onChange={handleChange}
required
rows={5}
style={{
width: '100%',
padding: '0.75rem 1rem',
borderRadius: '0.5rem',
border: '1px solid var(--sand)',
outline: 'none',
resize: 'vertical',
backgroundColor: 'var(--warm-white)'
}}
placeholder="How can we help you?"
></textarea>
</div>
<button
type="submit"
disabled={isSubmitting}
style={{
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'var(--sunset-orange)',
color: 'var(--warm-white)',
padding: '0.75rem 1.5rem',
borderRadius: '9999px',
fontWeight: 500,
border: 'none',
cursor: 'pointer',
transition: 'background-color 0.3s ease'
}}
onMouseOver={(e) => e.currentTarget.style.backgroundColor = 'var(--sunset-pink)'}
onMouseOut={(e) => e.currentTarget.style.backgroundColor = 'var(--sunset-orange)'}
>
{isSubmitting ? (
<>
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Sending...
</>
) : (
<>
Send Message
<Send style={{ marginLeft: '0.5rem', height: '1.25rem', width: '1.25rem' }} />
</>
)}
</button>
</form>
)}
</div>
</motion.div>
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,114 @@
'use client';
import { motion } from 'framer-motion';
import { Sun, Umbrella, Users, Award, Clock, Heart } from 'lucide-react';
interface FeatureCardProps {
icon: React.ReactNode;
title: string;
description: string;
index: number;
}
const features = [
{
icon: <Sun className="h-8 w-8 text-sunset-orange" />,
title: 'Summer Vibes All Year',
description: 'We bring the warm, relaxed feeling of summer to everything we do, no matter the season.',
},
{
icon: <Umbrella className="h-8 w-8 text-sunset-orange" />,
title: 'Relaxed Atmosphere',
description: 'Enjoy our laid-back, stress-free environment that makes you feel like you\'re on vacation.',
},
{
icon: <Users className="h-8 w-8 text-sunset-orange" />,
title: 'Family-Focused',
description: 'As a family business, we understand the importance of creating experiences for the whole family.',
},
{
icon: <Award className="h-8 w-8 text-sunset-orange" />,
title: 'Quality Guaranteed',
description: 'We stand behind our work with a 100% satisfaction guarantee on all our services.',
},
{
icon: <Clock className="h-8 w-8 text-sunset-orange" />,
title: 'Prompt Service',
description: 'We respect your time and always strive to provide timely, efficient service.',
},
{
icon: <Heart className="h-8 w-8 text-sunset-orange" />,
title: 'Community Love',
description: 'We\'re proud to be part of this community and give back through various local initiatives.',
},
];
function FeatureCard({ icon, title, description, index }: FeatureCardProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
viewport={{ once: true }}
className="bg-warm-white p-6 rounded-xl shadow-md hover:shadow-lg transition-shadow duration-300 border border-sand"
>
<div className="w-14 h-14 bg-sunset-orange/10 rounded-full flex items-center justify-center mb-4">
{icon}
</div>
<h3 className="text-xl text-night-blue mb-2" style={{ fontWeight: 700 }}>{title}</h3>
<p className="text-evening-blue/80">{description}</p>
</motion.div>
);
}
export function FeaturesSection() {
return (
<section id="services" className="section bg-sand/30">
<div className="container-custom">
<div className="text-center max-w-3xl mx-auto mb-16">
<motion.span
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
transition={{ duration: 0.5 }}
viewport={{ once: true }}
className="inline-block px-4 py-2 bg-sunset-orange/10 text-sunset-orange rounded-full mb-4"
style={{ fontWeight: 500 }}
>
Our Services
</motion.span>
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.1 }}
viewport={{ once: true }}
className="text-3xl md:text-4xl text-night-blue mb-4"
style={{ fontWeight: 700 }}
>
What Makes Us Special
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
viewport={{ once: true }}
className="text-evening-blue/80"
>
We combine the warmth of summer with exceptional service to create memorable experiences for your family.
</motion.p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{features.map((feature, index) => (
<FeatureCard
key={index}
icon={feature.icon}
title={feature.title}
description={feature.description}
index={index}
/>
))}
</div>
</div>
</section>
);
}

99
src/components/footer.tsx Normal file
View File

@ -0,0 +1,99 @@
'use client';
import Link from 'next/link';
import { Facebook, Instagram, Twitter, Mail, Phone, MapPin } from 'lucide-react';
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="bg-night-blue text-warm-white">
<div className="container-custom py-16">
<div className="grid grid-cols-1 md:grid-cols-3 gap-12">
{/* Company Info */}
<div className="space-y-4">
<div className="flex items-center space-x-2">
<div className="relative w-10 h-10">
<div className="absolute inset-0 bg-sunset-orange rounded-full"></div>
<div className="absolute inset-1 bg-warm-white rounded-full flex items-center justify-center">
<span className="text-sunset-orange text-lg" style={{ fontWeight: 700 }}>FB</span>
</div>
</div>
<span className="text-xl text-warm-white" style={{ fontWeight: 700 }}>Family Business</span>
</div>
<p className="text-sand/80 max-w-xs">
A family-owned business bringing summer vibes and quality service since 2005.
</p>
<div className="flex space-x-4">
<Link href="#" className="text-sand hover:text-sunset-orange transition-colors duration-300">
<Facebook size={20} />
</Link>
<Link href="#" className="text-sand hover:text-sunset-orange transition-colors duration-300">
<Instagram size={20} />
</Link>
<Link href="#" className="text-sand hover:text-sunset-orange transition-colors duration-300">
<Twitter size={20} />
</Link>
</div>
</div>
{/* Quick Links */}
<div className="space-y-4">
<h3 className="text-xl" style={{ fontWeight: 700 }}>Quick Links</h3>
<ul className="space-y-2">
<li>
<Link href="/" className="text-sand hover:text-sunset-orange transition-colors duration-300">
Home
</Link>
</li>
<li>
<Link href="#about" className="text-sand hover:text-sunset-orange transition-colors duration-300">
About Us
</Link>
</li>
<li>
<Link href="#services" className="text-sand hover:text-sunset-orange transition-colors duration-300">
Our Services
</Link>
</li>
<li>
<Link href="#gallery" className="text-sand hover:text-sunset-orange transition-colors duration-300">
Gallery
</Link>
</li>
<li>
<Link href="#contact" className="text-sand hover:text-sunset-orange transition-colors duration-300">
Contact
</Link>
</li>
</ul>
</div>
{/* Contact Info */}
<div className="space-y-4">
<h3 className="text-xl" style={{ fontWeight: 700 }}>Contact Us</h3>
<ul className="space-y-3">
<li className="flex items-start space-x-3">
<MapPin size={20} className="text-sunset-orange flex-shrink-0 mt-1" />
<span className="text-sand">123 Summer Lane, Beachside, CA 90210</span>
</li>
<li className="flex items-center space-x-3">
<Phone size={20} className="text-sunset-orange flex-shrink-0" />
<span className="text-sand">(555) 123-4567</span>
</li>
<li className="flex items-center space-x-3">
<Mail size={20} className="text-sunset-orange flex-shrink-0" />
<span className="text-sand">hello@familybusiness.com</span>
</li>
</ul>
</div>
</div>
{/* Copyright */}
<div className="border-t border-evening-blue/30 mt-12 pt-8 text-center text-sand/60">
<p>© {currentYear} Family Business. All rights reserved.</p>
</div>
</div>
</footer>
);
}

View File

@ -0,0 +1,195 @@
'use client';
import { motion } from 'framer-motion';
import Link from 'next/link';
import Image from 'next/image';
export function HeroSection() {
return (
<section style={{
position: 'relative',
overflow: 'hidden',
background: 'linear-gradient(to bottom, var(--warm-white), var(--sand))',
minHeight: '90vh',
display: 'flex',
alignItems: 'center'
}}>
{/* Background Elements */}
<div style={{ position: 'absolute', inset: 0, overflow: 'hidden' }}>
{/* Sun */}
<div style={{
position: 'absolute',
right: '-5rem',
top: '-5rem',
width: '16rem',
height: '16rem',
borderRadius: '9999px',
backgroundColor: 'var(--sunset-yellow)',
opacity: 0.2,
filter: 'blur(3rem)'
}}></div>
{/* Abstract shapes */}
<motion.div
style={{
position: 'absolute',
bottom: 0,
left: 0,
width: '100%',
height: '8rem',
backgroundColor: 'var(--sunset-orange)',
opacity: 0.1
}}
initial={{ y: 100 }}
animate={{ y: 0 }}
transition={{ duration: 1.5, ease: "easeOut" }}
></motion.div>
<motion.div
style={{
position: 'absolute',
top: '25%',
right: '25%',
width: '16rem',
height: '16rem',
borderRadius: '9999px',
backgroundColor: 'var(--sunset-pink)',
opacity: 0.1
}}
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ duration: 1.5, ease: "easeOut" }}
></motion.div>
</div>
<div className="container-custom" style={{ position: 'relative', zIndex: 10 }}>
<div style={{
display: 'grid',
gridTemplateColumns: '1fr',
gap: '3rem',
alignItems: 'center'
}} className="lg:grid-cols-2">
{/* Text Content */}
<motion.div
style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8, delay: 0.2 }}
>
<span style={{
display: 'inline-block',
padding: '0.5rem 1rem',
backgroundColor: 'rgba(255, 123, 84, 0.1)',
color: 'var(--sunset-orange)',
borderRadius: '9999px',
fontWeight: 500
}}>
Family Owned Since 2005
</span>
<h1 style={{
fontSize: 'clamp(2.25rem, 5vw, 3.75rem)',
fontWeight: 700,
color: 'var(--night-blue)',
lineHeight: 1.2
}}>
Bringing <span style={{ color: 'var(--sunset-orange)' }}>Summer Vibes</span> To Your Family
</h1>
<p style={{
fontSize: '1.125rem',
color: 'rgba(26, 95, 122, 0.8)',
maxWidth: '36rem'
}}>
We're a family business dedicated to providing exceptional service with the warm, relaxed feeling of a perfect summer evening.
</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1rem', paddingTop: '1rem' }}>
<Link href="#services" className="btn-primary">
Our Services
</Link>
<Link href="#contact" className="btn-secondary">
Contact Us
</Link>
</div>
{/* Trust Indicators */}
<div style={{ paddingTop: '2rem', display: 'flex', alignItems: 'center', gap: '1.5rem' }}>
<div style={{ display: 'flex' }}>
{[1, 2, 3, 4].map((i) => (
<div key={i} style={{
width: '2.5rem',
height: '2.5rem',
borderRadius: '9999px',
backgroundColor: 'var(--sunset-orange)',
border: '2px solid var(--warm-white)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'var(--warm-white)',
fontWeight: 700,
marginLeft: i > 1 ? '-0.5rem' : 0
}}>
{i}
</div>
))}
</div>
<div>
<p style={{ fontWeight: 500, color: 'var(--evening-blue)' }}>Trusted by 2,000+ families</p>
<div style={{ display: 'flex', alignItems: 'center', color: 'var(--sunset-yellow)' }}>
{[1, 2, 3, 4, 5].map((i) => (
<svg key={i} xmlns="http://www.w3.org/2000/svg" style={{ height: '1.25rem', width: '1.25rem' }} viewBox="0 0 20 20" fill="currentColor">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
))}
</div>
</div>
</div>
</motion.div>
{/* Image */}
<motion.div
style={{
position: 'relative',
height: '500px',
borderRadius: '1rem',
overflow: 'hidden',
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)'
}}
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.5 }}
>
<Image
src="/images/hero.jpg"
alt="Family Business"
fill
style={{ objectFit: 'cover' }}
priority
/>
<div style={{
position: 'absolute',
inset: 0,
background: 'linear-gradient(to top, rgba(0, 43, 91, 0.4), transparent)'
}}></div>
<div style={{
position: 'absolute',
bottom: '1.5rem',
left: '1.5rem',
right: '1.5rem',
padding: '1.5rem',
backgroundColor: 'rgba(255, 248, 234, 0.9)',
backdropFilter: 'blur(4px)',
borderRadius: '0.75rem'
}}>
<h3 style={{
fontWeight: 700,
color: 'var(--night-blue)',
fontSize: '1.25rem',
marginBottom: '0.5rem'
}}>Summer Special Offer</h3>
<p style={{ color: 'var(--evening-blue)' }}>Get 20% off on all our premium services until August 31st!</p>
</div>
</motion.div>
</div>
</div>
</section>
);
}

160
src/components/navbar.tsx Normal file
View File

@ -0,0 +1,160 @@
'use client';
import { useState } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { motion } from 'framer-motion';
import { Menu, X } from 'lucide-react';
interface NavItem {
label: string;
href: string;
}
const navItems: NavItem[] = [
{ label: 'Home', href: '/' },
{ label: 'About', href: '#about' },
{ label: 'Services', href: '#services' },
{ label: 'Gallery', href: '#gallery' },
{ label: 'Contact', href: '#contact' },
];
export function Navbar() {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen(!isOpen);
};
return (
<nav style={{
position: 'sticky',
top: 0,
zIndex: 50,
backgroundColor: 'rgba(255, 248, 234, 0.9)',
backdropFilter: 'blur(4px)',
borderBottom: '1px solid var(--sand)'
}}>
<div className="container-custom" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '1rem 0' }}>
{/* Logo */}
<Link href="/" style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<div style={{ position: 'relative', width: '2.5rem', height: '2.5rem' }}>
<div style={{
position: 'absolute',
inset: 0,
backgroundColor: 'var(--sunset-orange)',
borderRadius: '9999px'
}}></div>
<div style={{
position: 'absolute',
inset: '0.25rem',
backgroundColor: 'var(--warm-white)',
borderRadius: '9999px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
<span style={{
color: 'var(--sunset-orange)',
fontSize: '1.125rem',
fontWeight: 700
}}>FB</span>
</div>
</div>
<span style={{
fontSize: '1.25rem',
fontWeight: 700,
color: 'var(--night-blue)'
}}>Family Business</span>
</Link>
{/* Desktop Navigation */}
<div style={{
display: 'none',
alignItems: 'center',
gap: '2rem'
}} className="hidden md:flex">
{navItems.map((item) => (
<Link
key={item.label}
href={item.href}
style={{
color: 'var(--evening-blue)',
fontWeight: 500,
transition: 'color 0.3s ease'
}}
onMouseOver={(e) => e.currentTarget.style.color = 'var(--sunset-orange)'}
onMouseOut={(e) => e.currentTarget.style.color = 'var(--evening-blue)'}
>
{item.label}
</Link>
))}
<Link href="#contact" className="btn-primary">
Get in Touch
</Link>
</div>
{/* Mobile Menu Button */}
<button
style={{
display: 'block',
color: 'var(--evening-blue)'
}}
className="md:hidden"
onClick={toggleMenu}
aria-label="Toggle menu"
>
{isOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
{/* Mobile Navigation */}
{isOpen && (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
style={{
backgroundColor: 'var(--warm-white)',
borderTop: '1px solid var(--sand)'
}}
className="md:hidden"
>
<div className="container-custom" style={{
padding: '1rem 0',
display: 'flex',
flexDirection: 'column',
gap: '1rem'
}}>
{navItems.map((item) => (
<Link
key={item.label}
href={item.href}
style={{
color: 'var(--evening-blue)',
fontWeight: 500,
padding: '0.5rem 0',
transition: 'color 0.3s ease'
}}
onClick={() => setIsOpen(false)}
onMouseOver={(e) => e.currentTarget.style.color = 'var(--sunset-orange)'}
onMouseOut={(e) => e.currentTarget.style.color = 'var(--evening-blue)'}
>
{item.label}
</Link>
))}
<Link
href="#contact"
className="btn-primary"
style={{ display: 'inline-block', textAlign: 'center' }}
onClick={() => setIsOpen(false)}
>
Get in Touch
</Link>
</div>
</motion.div>
)}
</nav>
);
}

30
tailwind.config.js Normal file
View File

@ -0,0 +1,30 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {
colors: {
// Summer sunset palette
'sunset-orange': '#FF7B54', // Warm orange
'sunset-pink': '#FFB26B', // Soft pink/peach
'sunset-yellow': '#FFD56F', // Golden yellow
'sunset-purple': '#939B62', // Dusky purple/green
'evening-blue': '#1A5F7A', // Deep evening blue
'night-blue': '#002B5B', // Night sky blue
'warm-white': '#FFF8EA', // Warm white
'sand': '#E1D7C6', // Sandy beige
},
fontFamily: {
sans: ['var(--font-inter)', 'sans-serif'],
display: ['var(--font-montserrat)', 'sans-serif'],
},
backgroundImage: {
'gradient-sunset': 'linear-gradient(to right, var(--tw-colors-sunset-orange), var(--tw-colors-sunset-pink))',
'gradient-evening': 'linear-gradient(to right, var(--tw-colors-evening-blue), var(--tw-colors-night-blue))',
},
},
},
plugins: [],
}

28
tsconfig.json Normal file
View File

@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}