Refactor project structure: update Astro configuration, integrate Tailwind CSS, enhance ContactForm with validation, and improve layout for various pages. Add new Donate and Orphanage pages, and implement responsive design adjustments across components.
This commit is contained in:
@@ -1,15 +1,13 @@
|
|||||||
// @ts-check
|
// astro.config.mjs
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
import tailwind from '@astrojs/tailwind';
|
||||||
import tailwindcss from '@tailwindcss/vite';
|
|
||||||
|
|
||||||
import react from '@astrojs/react';
|
import react from '@astrojs/react';
|
||||||
|
|
||||||
// https://astro.build/config
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
vite: {
|
integrations: [
|
||||||
plugins: [tailwindcss()]
|
react(),
|
||||||
},
|
tailwind({
|
||||||
|
applyBaseStyles: false, // Prevents conflicts with custom styles
|
||||||
integrations: [react()]
|
})
|
||||||
|
],
|
||||||
});
|
});
|
1247
package-lock.json
generated
1247
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,10 +14,12 @@
|
|||||||
"@types/react": "^19.1.8",
|
"@types/react": "^19.1.8",
|
||||||
"@types/react-dom": "^19.1.6",
|
"@types/react-dom": "^19.1.6",
|
||||||
"astro": "^5.9.4",
|
"astro": "^5.9.4",
|
||||||
"daisyui": "^5.0.43",
|
|
||||||
"keen-slider": "^6.8.6",
|
"keen-slider": "^6.8.6",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"tailwindcss": "^4.1.10"
|
"tailwindcss": "^3.4.17"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@astrojs/tailwind": "^6.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,46 +1,140 @@
|
|||||||
import { useState } from "react";
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
export default function ContactForm() {
|
export default function ContactForm() {
|
||||||
const [status, setStatus] = useState("");
|
const [formData, setFormData] = useState({
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
subject: '',
|
||||||
|
message: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [errors, setErrors] = useState({});
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [submitMessage, setSubmitMessage] = useState(null);
|
||||||
|
|
||||||
|
const validate = () => {
|
||||||
|
let newErrors = {};
|
||||||
|
if (!formData.name) newErrors.name = 'Name is required';
|
||||||
|
if (!formData.email) {
|
||||||
|
newErrors.email = 'Email is required';
|
||||||
|
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(formData.email)) {
|
||||||
|
newErrors.email = 'Invalid email address';
|
||||||
|
}
|
||||||
|
if (!formData.subject) newErrors.subject = 'Subject is required';
|
||||||
|
if (!formData.message) newErrors.message = 'Message is required';
|
||||||
|
return newErrors;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setFormData({ ...formData, [name]: value });
|
||||||
|
// Clear error for the field as user types
|
||||||
|
if (errors[name]) {
|
||||||
|
setErrors({ ...errors, [name]: null });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const form = e.target;
|
setIsSubmitting(true);
|
||||||
// Honeypot check
|
setSubmitMessage(null);
|
||||||
if (form.honey.value !== "") {
|
|
||||||
setStatus("Spam detected.");
|
const validationErrors = validate();
|
||||||
return;
|
setErrors(validationErrors);
|
||||||
|
|
||||||
|
if (Object.keys(validationErrors).length === 0) {
|
||||||
|
try {
|
||||||
|
// Simulate API call
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
console.log('Form data submitted:', formData);
|
||||||
|
setSubmitMessage({ type: 'success', text: 'Your message has been sent successfully!' });
|
||||||
|
setFormData({ name: '', email: '', subject: '', message: '' }); // Clear form
|
||||||
|
} catch (error) {
|
||||||
|
setSubmitMessage({ type: 'error', text: 'There was an error sending your message. Please try again later.' });
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
setSubmitMessage({ type: 'error', text: 'Please correct the errors in the form.' });
|
||||||
}
|
}
|
||||||
setStatus("Sending...");
|
|
||||||
// Placeholder: would POST to /api/contact
|
|
||||||
setTimeout(() => setStatus("Message sent (placeholder, not yet implemented)."), 1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="max-w-xl mx-auto bg-base-100 p-8 rounded-xl shadow-lg" onSubmit={handleSubmit} autoComplete="off">
|
<form onSubmit={handleSubmit} className="bg-white p-8 rounded-xl shadow-lg border border-gray-200 max-w-2xl mx-auto">
|
||||||
<div className="mb-4">
|
<h2 className="text-3xl font-bold text-center text-nigerian-green-700 mb-8">Send Us a Message</h2>
|
||||||
<label htmlFor="name" className="block font-bold mb-1">Name</label>
|
|
||||||
<input type="text" id="name" name="name" className="input input-bordered w-full" required autoComplete="off" />
|
{submitMessage && (
|
||||||
|
<div className={`p-4 mb-4 rounded-lg text-center ${submitMessage.type === 'success' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>
|
||||||
|
{submitMessage.text}
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4">
|
)}
|
||||||
<label htmlFor="email" className="block font-bold mb-1">Email</label>
|
|
||||||
<input type="email" id="email" name="email" className="input input-bordered w-full" required autoComplete="off" />
|
<div className="mb-5">
|
||||||
|
<label htmlFor="name" className="block text-gray-700 text-sm font-bold mb-2">Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
value={formData.name}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={`shadow appearance-none border rounded-lg w-full py-3 px-4 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-nigerian-green-500 ${errors.name ? 'border-red-500' : 'border-gray-300'}`}
|
||||||
|
placeholder="Your Name"
|
||||||
|
/>
|
||||||
|
{errors.name && <p className="text-red-500 text-xs italic mt-2">{errors.name}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4">
|
|
||||||
<label htmlFor="subject" className="block font-bold mb-1">Subject</label>
|
<div className="mb-5">
|
||||||
<input type="text" id="subject" name="subject" className="input input-bordered w-full" required autoComplete="off" />
|
<label htmlFor="email" className="block text-gray-700 text-sm font-bold mb-2">Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={`shadow appearance-none border rounded-lg w-full py-3 px-4 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-nigerian-green-500 ${errors.email ? 'border-red-500' : 'border-gray-300'}`}
|
||||||
|
placeholder="your.email@example.com"
|
||||||
|
/>
|
||||||
|
{errors.email && <p className="text-red-500 text-xs italic mt-2">{errors.email}</p>}
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4">
|
|
||||||
<label htmlFor="message" className="block font-bold mb-1">Message</label>
|
<div className="mb-5">
|
||||||
<textarea id="message" name="message" className="textarea textarea-bordered w-full" rows={5} required></textarea>
|
<label htmlFor="subject" className="block text-gray-700 text-sm font-bold mb-2">Subject</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="subject"
|
||||||
|
name="subject"
|
||||||
|
value={formData.subject}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={`shadow appearance-none border rounded-lg w-full py-3 px-4 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-nigerian-green-500 ${errors.subject ? 'border-red-500' : 'border-gray-300'}`}
|
||||||
|
placeholder="Subject of your message"
|
||||||
|
/>
|
||||||
|
{errors.subject && <p className="text-red-500 text-xs italic mt-2">{errors.subject}</p>}
|
||||||
</div>
|
</div>
|
||||||
{/* Honeypot field for spam protection */}
|
|
||||||
<div style={{ display: "none" }}>
|
<div className="mb-6">
|
||||||
<label htmlFor="honey">Do not fill this out</label>
|
<label htmlFor="message" className="block text-gray-700 text-sm font-bold mb-2">Message</label>
|
||||||
<input type="text" id="honey" name="honey" tabIndex="-1" autoComplete="off" />
|
<textarea
|
||||||
|
id="message"
|
||||||
|
name="message"
|
||||||
|
rows="6"
|
||||||
|
value={formData.message}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={`shadow appearance-none border rounded-lg w-full py-3 px-4 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-nigerian-green-500 ${errors.message ? 'border-red-500' : 'border-gray-300'}`}
|
||||||
|
placeholder="Your message..."
|
||||||
|
></textarea>
|
||||||
|
{errors.message && <p className="text-red-500 text-xs italic mt-2">{errors.message}</p>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="bg-gradient-to-r from-nigerian-green-500 to-kente-gold-500 hover:from-nigerian-green-600 hover:to-kente-gold-600 text-white font-bold py-3 px-8 rounded-lg focus:outline-none focus:shadow-outline transition-all duration-300 transform hover:scale-105"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
{isSubmitting ? 'Sending...' : 'Send Message'}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" className="btn btn-primary w-full">Send Message</button>
|
|
||||||
{status && <div className="mt-4 text-center text-sm text-success">{status}</div>}
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
85
src/components/DonationProgressBar.jsx
Normal file
85
src/components/DonationProgressBar.jsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
const MONTHLY_GOAL = 5000; // €5000 monthly goal
|
||||||
|
|
||||||
|
export default function DonationProgressBar() {
|
||||||
|
const [currentAmount, setCurrentAmount] = useState(0);
|
||||||
|
const [lastResetDate, setLastResetDate] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Load data from localStorage
|
||||||
|
const storedAmount = localStorage.getItem('omoluabi_donation_amount');
|
||||||
|
const storedResetDate = localStorage.getItem('omoluabi_donation_last_reset');
|
||||||
|
|
||||||
|
let amount = storedAmount ? parseFloat(storedAmount) : 0;
|
||||||
|
let resetDate = storedResetDate ? new Date(storedResetDate) : null;
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const firstDayOfCurrentMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||||
|
|
||||||
|
// Check if it's a new month or if resetDate is not set
|
||||||
|
if (!resetDate || resetDate.getMonth() !== now.getMonth() || resetDate.getFullYear() !== now.getFullYear()) {
|
||||||
|
// Reset for the new month
|
||||||
|
amount = 0;
|
||||||
|
resetDate = firstDayOfCurrentMonth;
|
||||||
|
localStorage.setItem('omoluabi_donation_amount', amount.toString());
|
||||||
|
localStorage.setItem('omoluabi_donation_last_reset', resetDate.toISOString());
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentAmount(amount);
|
||||||
|
setLastResetDate(resetDate);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const percentage = Math.min(100, (currentAmount / MONTHLY_GOAL) * 100);
|
||||||
|
|
||||||
|
// This function is for demonstration purposes. In a real app, donations would come from a payment gateway.
|
||||||
|
const handleDonate = (amount) => {
|
||||||
|
setCurrentAmount(prevAmount => {
|
||||||
|
const newAmount = prevAmount + amount;
|
||||||
|
localStorage.setItem('omoluabi_donation_amount', newAmount.toString());
|
||||||
|
return newAmount;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white p-6 rounded-xl shadow-lg border border-gray-200">
|
||||||
|
<h2 className="text-2xl font-bold text-center mb-4 text-nigerian-green-700">Monthly Donation Goal</h2>
|
||||||
|
<p className="text-center text-gray-600 mb-6">Help us reach our goal to support our initiatives!</p>
|
||||||
|
|
||||||
|
<div className="w-full bg-gray-200 rounded-full h-4 mb-4 overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-4 rounded-full bg-gradient-to-r from-nigerian-green-500 to-kente-gold-500 transition-all duration-1000 ease-out"
|
||||||
|
style={{ width: `${percentage}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center mb-6">
|
||||||
|
<span className="text-lg font-semibold text-gray-800">€{currentAmount.toLocaleString()}</span>
|
||||||
|
<span className="text-lg font-semibold text-gray-800">€{MONTHLY_GOAL.toLocaleString()}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center text-sm text-gray-500 mb-6">
|
||||||
|
{percentage.toFixed(1)}% of the goal reached this month.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3 mb-6">
|
||||||
|
{[10, 25, 50, 100, 250, 500].map(amount => (
|
||||||
|
<button
|
||||||
|
key={amount}
|
||||||
|
onClick={() => handleDonate(amount)}
|
||||||
|
className="bg-nigerian-green-100 text-nigerian-green-700 font-semibold py-2 px-4 rounded-lg hover:bg-nigerian-green-200 transition-colors duration-200 shadow-sm"
|
||||||
|
>
|
||||||
|
Donate €{amount}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => handleDonate(100)} // Example: default donation
|
||||||
|
className="w-full bg-gradient-to-r from-ankara-red-500 to-kente-gold-500 text-white font-bold py-3 rounded-lg shadow-md hover:shadow-lg transform hover:scale-105 transition-all duration-300"
|
||||||
|
>
|
||||||
|
Make a Custom Donation
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
82
src/components/EventFilterSearch.jsx
Normal file
82
src/components/EventFilterSearch.jsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
export default function EventFilterSearch({ events }) {
|
||||||
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState('All');
|
||||||
|
const [filteredEvents, setFilteredEvents] = useState(events);
|
||||||
|
|
||||||
|
const categories = ['All', ...new Set(events.map(event => event.frontmatter.category))];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let tempEvents = events;
|
||||||
|
|
||||||
|
// Filter by category
|
||||||
|
if (selectedCategory !== 'All') {
|
||||||
|
tempEvents = tempEvents.filter(event => event.frontmatter.category === selectedCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by search term
|
||||||
|
if (searchTerm) {
|
||||||
|
tempEvents = tempEvents.filter(event =>
|
||||||
|
event.frontmatter.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
|
event.frontmatter.description.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredEvents(tempEvents);
|
||||||
|
}, [searchTerm, selectedCategory, events]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="flex flex-col md:flex-row gap-4 mb-8">
|
||||||
|
{/* Search Input */}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search events..."
|
||||||
|
className="flex-grow p-3 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-nigerian-green-500"
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Category Filter */}
|
||||||
|
<select
|
||||||
|
className="p-3 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-nigerian-green-500"
|
||||||
|
value={selectedCategory}
|
||||||
|
onChange={(e) => setSelectedCategory(e.target.value)}
|
||||||
|
>
|
||||||
|
{categories.map(category => (
|
||||||
|
<option key={category} value={category}>{category}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Event List */}
|
||||||
|
<section className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
|
{filteredEvents.length > 0 ? (
|
||||||
|
filteredEvents.map(event => (
|
||||||
|
<article key={event.url} className="card bg-base-100 shadow-lg rounded-xl overflow-hidden transform transition-transform duration-300 hover:scale-105 hover:shadow-xl">
|
||||||
|
<figure className="relative h-48 w-full overflow-hidden">
|
||||||
|
<img src={event.frontmatter.image} alt={event.frontmatter.title} className="w-full h-full object-cover" />
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent"></div>
|
||||||
|
<span className="absolute bottom-3 left-3 badge badge-secondary bg-kente-gold-500 text-white px-3 py-1 rounded-full text-sm font-semibold">{event.frontmatter.category}</span>
|
||||||
|
</figure>
|
||||||
|
<div className="card-body p-6">
|
||||||
|
<h2 className="card-title text-xl font-bold text-nigerian-green-700 mb-2">{event.frontmatter.title}</h2>
|
||||||
|
<p className="text-sm text-gray-600 mb-3">{new Date(event.frontmatter.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</p>
|
||||||
|
<p className="text-gray-700 leading-relaxed text-sm mb-4">{event.frontmatter.description}</p>
|
||||||
|
<a href={event.url} className="inline-flex items-center text-nigerian-green-600 hover:text-nigerian-green-800 font-semibold transition-colors duration-200">
|
||||||
|
Read More
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<p className="text-center text-gray-500 text-lg col-span-full">No events found matching your criteria.</p>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@@ -95,6 +95,9 @@
|
|||||||
<a href="/orphanage" class="block text-gray-300 hover:text-nigerian-green-400 transition-colors duration-200 hover:translate-x-1 transform">
|
<a href="/orphanage" class="block text-gray-300 hover:text-nigerian-green-400 transition-colors duration-200 hover:translate-x-1 transform">
|
||||||
Orphanage
|
Orphanage
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/gallery" class="block text-gray-300 hover:text-nigerian-green-400 transition-colors duration-200 hover:translate-x-1 transform">
|
||||||
|
Gallery
|
||||||
|
</a>
|
||||||
<a href="/contact" class="block text-gray-300 hover:text-nigerian-green-400 transition-colors duration-200 hover:translate-x-1 transform">
|
<a href="/contact" class="block text-gray-300 hover:text-nigerian-green-400 transition-colors duration-200 hover:translate-x-1 transform">
|
||||||
Contact
|
Contact
|
||||||
</a>
|
</a>
|
||||||
|
@@ -73,6 +73,10 @@
|
|||||||
Orphanage
|
Orphanage
|
||||||
<span class="absolute bottom-0 left-0 w-0 h-0.5 bg-green-600 group-hover:w-full transition-all duration-300"></span>
|
<span class="absolute bottom-0 left-0 w-0 h-0.5 bg-green-600 group-hover:w-full transition-all duration-300"></span>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/gallery" class="nav-link px-4 py-2 rounded-lg font-medium text-gray-700 hover:text-green-600 hover:bg-green-50 transition-all duration-200 relative group">
|
||||||
|
Gallery
|
||||||
|
<span class="absolute bottom-0 left-0 w-0 h-0.5 bg-green-600 group-hover:w-full transition-all duration-300"></span>
|
||||||
|
</a>
|
||||||
<a href="/contact" class="nav-link px-4 py-2 rounded-lg font-medium text-gray-700 hover:text-green-600 hover:bg-green-50 transition-all duration-200 relative group">
|
<a href="/contact" class="nav-link px-4 py-2 rounded-lg font-medium text-gray-700 hover:text-green-600 hover:bg-green-50 transition-all duration-200 relative group">
|
||||||
Contact
|
Contact
|
||||||
<span class="absolute bottom-0 left-0 w-0 h-0.5 bg-green-600 group-hover:w-full transition-all duration-300"></span>
|
<span class="absolute bottom-0 left-0 w-0 h-0.5 bg-green-600 group-hover:w-full transition-all duration-300"></span>
|
||||||
@@ -122,6 +126,9 @@
|
|||||||
<a href="/orphanage" class="block px-4 py-3 rounded-lg font-medium text-gray-700 hover:text-green-600 hover:bg-green-50 transition-all duration-200">
|
<a href="/orphanage" class="block px-4 py-3 rounded-lg font-medium text-gray-700 hover:text-green-600 hover:bg-green-50 transition-all duration-200">
|
||||||
🏠 Orphanage
|
🏠 Orphanage
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/gallery" class="block px-4 py-3 rounded-lg font-medium text-gray-700 hover:text-green-600 hover:bg-green-50 transition-all duration-200">
|
||||||
|
🖼️ Gallery
|
||||||
|
</a>
|
||||||
<a href="/contact" class="block px-4 py-3 rounded-lg font-medium text-gray-700 hover:text-green-600 hover:bg-green-50 transition-all duration-200">
|
<a href="/contact" class="block px-4 py-3 rounded-lg font-medium text-gray-700 hover:text-green-600 hover:bg-green-50 transition-all duration-200">
|
||||||
📞 Contact
|
📞 Contact
|
||||||
</a>
|
</a>
|
||||||
|
@@ -2,13 +2,24 @@
|
|||||||
const gallery = await Astro.glob('../../content/gallery/gallery-sample.md');
|
const gallery = await Astro.glob('../../content/gallery/gallery-sample.md');
|
||||||
const images = gallery[0]?.frontmatter.images || [];
|
const images = gallery[0]?.frontmatter.images || [];
|
||||||
---
|
---
|
||||||
<section class="max-w-6xl mx-auto my-20">
|
<section class="max-w-6xl mx-auto my-20 px-4">
|
||||||
<h2 class="text-2xl font-headline font-bold mb-6 text-primary text-center">Gallery</h2>
|
<h2 class="text-3xl font-headline font-bold mb-6 text-primary text-center">Our Photo Gallery</h2>
|
||||||
|
<p class="text-center text-lg text-gray-600 mb-10">
|
||||||
|
A glimpse into our vibrant community and cultural celebrations.
|
||||||
|
</p>
|
||||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
{images.map((img, idx) => (
|
{images.slice(0, 4).map((img, idx) => (
|
||||||
<div class="rounded-lg overflow-hidden shadow-md bg-base-100" key={idx}>
|
<div class="rounded-lg overflow-hidden shadow-md bg-base-100" key={idx}>
|
||||||
<img src={img} alt={`Gallery photo ${idx + 1}`} class="w-full h-40 object-cover hover:scale-105 transition-transform duration-200 cursor-pointer" />
|
<img src={img} alt={`Gallery photo ${idx + 1}`} class="w-full h-40 object-cover hover:scale-105 transition-transform duration-200 cursor-pointer" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="text-center mt-10">
|
||||||
|
<a href="/gallery" class="inline-flex items-center px-6 py-3 bg-gradient-to-r from-nigerian-green-500 to-kente-gold-500 text-white rounded-lg shadow-lg hover:shadow-xl hover:from-nigerian-green-600 hover:to-kente-gold-600 transition-all duration-200 font-medium">
|
||||||
|
View Full Gallery
|
||||||
|
<svg class="w-5 h-5 ml-2" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
58
src/components/ImageGallery.jsx
Normal file
58
src/components/ImageGallery.jsx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import Lightbox from './Lightbox.jsx'; // Assuming Lightbox component will be created
|
||||||
|
|
||||||
|
export default function ImageGallery({ images }) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [currentImageIndex, setCurrentImageIndex] = useState(0);
|
||||||
|
|
||||||
|
const openLightbox = (index) => {
|
||||||
|
setCurrentImageIndex(index);
|
||||||
|
setIsOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeLightbox = () => {
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToNext = () => {
|
||||||
|
setCurrentImageIndex((prevIndex) => (prevIndex + 1) % images.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToPrev = () => {
|
||||||
|
setCurrentImageIndex((prevIndex) => (prevIndex - 1 + images.length) % images.length);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
||||||
|
{images.map((image, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="relative overflow-hidden rounded-lg shadow-md cursor-pointer group"
|
||||||
|
onClick={() => openLightbox(index)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={image}
|
||||||
|
alt={`Gallery image ${index + 1}`}
|
||||||
|
className="w-full h-48 object-cover transform transition-transform duration-300 group-hover:scale-110"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-black bg-opacity-40 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||||
|
<svg className="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{isOpen && (
|
||||||
|
<Lightbox
|
||||||
|
images={images}
|
||||||
|
currentIndex={currentImageIndex}
|
||||||
|
onClose={closeLightbox}
|
||||||
|
onNext={goToNext}
|
||||||
|
onPrev={goToPrev}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
68
src/components/Lightbox.jsx
Normal file
68
src/components/Lightbox.jsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
export default function Lightbox({ images, currentIndex, onClose, onNext, onPrev }) {
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
onClose();
|
||||||
|
} else if (e.key === 'ArrowRight') {
|
||||||
|
onNext();
|
||||||
|
} else if (e.key === 'ArrowLeft') {
|
||||||
|
onPrev();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
};
|
||||||
|
}, [onClose, onNext, onPrev]);
|
||||||
|
|
||||||
|
if (!images || images.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-[9999] bg-black bg-opacity-90 flex items-center justify-center p-4" onClick={onClose}>
|
||||||
|
<div className="relative max-w-5xl max-h-full" onClick={(e) => e.stopPropagation()}> {/* Prevent closing when clicking on image */}
|
||||||
|
<img
|
||||||
|
src={images[currentIndex]}
|
||||||
|
alt={`Gallery image ${currentIndex + 1}`}
|
||||||
|
className="max-w-full max-h-[80vh] object-contain rounded-lg shadow-xl"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Close Button */}
|
||||||
|
<button
|
||||||
|
className="absolute top-4 right-4 text-white text-3xl p-2 rounded-full bg-white/20 hover:bg-white/40 transition-colors duration-200"
|
||||||
|
onClick={onClose}
|
||||||
|
aria-label="Close Lightbox"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Navigation Buttons */}
|
||||||
|
{images.length > 1 && (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
className="absolute left-4 top-1/2 -translate-y-1/2 text-white text-4xl p-2 rounded-full bg-white/20 hover:bg-white/40 transition-colors duration-200"
|
||||||
|
onClick={onPrev}
|
||||||
|
aria-label="Previous Image"
|
||||||
|
>
|
||||||
|
‹
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="absolute right-4 top-1/2 -translate-y-1/2 text-white text-4xl p-2 rounded-full bg-white/20 hover:bg-white/40 transition-colors duration-200"
|
||||||
|
onClick={onNext}
|
||||||
|
aria-label="Next Image"
|
||||||
|
>
|
||||||
|
›
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Image Counter */}
|
||||||
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 text-white text-lg bg-black/50 px-4 py-2 rounded-full">
|
||||||
|
{currentIndex + 1} / {images.length}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,41 +1,170 @@
|
|||||||
---
|
---
|
||||||
// Base layout for all pages
|
// src/layouts/BaseLayout.astro
|
||||||
import Header from '../components/Header.astro';
|
import '../styles/global.css';
|
||||||
import Footer from '../components/Footer.astro';
|
|
||||||
|
export interface Props {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title = "Omoluabi Association Netherlands", description = "Preserving Nigerian culture and heritage in the Netherlands" } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Omoluabi Foundation</title>
|
<title>{title}</title>
|
||||||
<meta name="description" content="Supporting Nigerians in the Netherlands" />
|
<meta name="description" content={description} />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Poppins:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<slot name="head" />
|
|
||||||
</head>
|
</head>
|
||||||
<body style="
|
|
||||||
font-family: 'Inter', system-ui, sans-serif;
|
<body class="bg-gray-50">
|
||||||
margin: 0;
|
<!-- Navigation Header -->
|
||||||
padding: 0;
|
<header class="bg-white shadow-sm sticky top-0 z-50">
|
||||||
background: linear-gradient(135deg, #fafafa 0%, #f0fdf4 100%);
|
<nav class="container mx-auto px-4 py-4">
|
||||||
min-height: 100vh;
|
<div class="flex items-center justify-between">
|
||||||
display: flex;
|
<!-- Logo -->
|
||||||
flex-direction: column;
|
<div class="flex items-center space-x-2">
|
||||||
padding-top: 80px;
|
<div class="w-10 h-10 bg-gradient-to-br from-nigerian-green-500 to-kente-gold-500 rounded-full flex items-center justify-center">
|
||||||
">
|
<span class="text-white font-bold text-lg">O</span>
|
||||||
<Header />
|
</div>
|
||||||
<main style="flex: 1;">
|
<div>
|
||||||
|
<div class="font-headline font-bold text-xl text-nigerian-green-700">Omoluabi</div>
|
||||||
|
<div class="text-sm text-gray-600">Foundation</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Desktop Navigation -->
|
||||||
|
<div class="hidden md:flex items-center space-x-8">
|
||||||
|
<a href="/" class="text-gray-700 hover:text-nigerian-green-600 transition-colors">Home</a>
|
||||||
|
<a href="/about" class="text-gray-700 hover:text-nigerian-green-600 transition-colors">About</a>
|
||||||
|
<a href="/events" class="text-gray-700 hover:text-nigerian-green-600 transition-colors">Events</a>
|
||||||
|
<a href="/members" class="text-gray-700 hover:text-nigerian-green-600 transition-colors">Members</a>
|
||||||
|
<a href="/orphanage" class="text-gray-700 hover:text-nigerian-green-600 transition-colors">Orphanage</a>
|
||||||
|
<a href="/gallery" class="text-gray-700 hover:text-nigerian-green-600 transition-colors">Gallery</a>
|
||||||
|
<a href="/contact" class="text-gray-700 hover:text-nigerian-green-600 transition-colors">Contact</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CTA Buttons -->
|
||||||
|
<div class="hidden md:flex items-center space-x-4">
|
||||||
|
<a href="/donate" class="btn bg-gradient-to-r from-ankara-red-500 to-kente-gold-500 text-white hover:shadow-lg">
|
||||||
|
❤️ Donate
|
||||||
|
</a>
|
||||||
|
<a href="/join" class="btn bg-gradient-to-r from-nigerian-green-500 to-kente-gold-500 text-white hover:shadow-lg">
|
||||||
|
⭐ Join Us
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile menu button -->
|
||||||
|
<button class="md:hidden" id="mobile-menu-button">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile menu -->
|
||||||
|
<div class="md:hidden hidden" id="mobile-menu">
|
||||||
|
<div class="pt-4 pb-2 space-y-2">
|
||||||
|
<a href="/" class="block px-3 py-2 text-gray-700 hover:text-nigerian-green-600">Home</a>
|
||||||
|
<a href="/about" class="block px-3 py-2 text-gray-700 hover:text-nigerian-green-600">About</a>
|
||||||
|
<a href="/events" class="block px-3 py-2 text-gray-700 hover:text-nigerian-green-600">Events</a>
|
||||||
|
<a href="/members" class="block px-3 py-2 text-gray-700 hover:text-nigerian-green-600">Members</a>
|
||||||
|
<a href="/orphanage" class="block px-3 py-2 text-gray-700 hover:text-nigerian-green-600">Orphanage</a>
|
||||||
|
<a href="/gallery" class="block px-3 py-2 text-gray-700 hover:text-nigerian-green-600">Gallery</a>
|
||||||
|
<a href="/contact" class="block px-3 py-2 text-gray-700 hover:text-nigerian-green-600">Contact</a>
|
||||||
|
<div class="pt-4 space-y-2">
|
||||||
|
<a href="/donate" class="block btn bg-gradient-to-r from-ankara-red-500 to-kente-gold-500 text-white text-center">❤️ Donate</a>
|
||||||
|
<a href="/join" class="block btn bg-gradient-to-r from-nigerian-green-500 to-kente-gold-500 text-white text-center">⭐ Join Us</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main>
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="bg-gray-900 text-white py-12">
|
||||||
|
<div class="container">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
<div class="flex items-center space-x-2 mb-4">
|
||||||
|
<div class="w-10 h-10 bg-gradient-to-br from-nigerian-green-500 to-kente-gold-500 rounded-full flex items-center justify-center">
|
||||||
|
<span class="text-white font-bold text-lg">O</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="font-headline font-bold text-xl">Omoluabi Foundation</div>
|
||||||
|
<div class="text-sm text-gray-400">Netherlands</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-gray-300 mb-4">
|
||||||
|
Preserving Nigerian culture and heritage while building stronger communities in the Netherlands.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 class="font-semibold mb-4">Quick Links</h3>
|
||||||
|
<ul class="space-y-2 text-sm">
|
||||||
|
<li><a href="/about" class="text-gray-300 hover:text-white transition-colors">About Us</a></li>
|
||||||
|
<li><a href="/events" class="text-gray-300 hover:text-white transition-colors">Events</a></li>
|
||||||
|
<li><a href="/members" class="text-gray-300 hover:text-white transition-colors">Members</a></li>
|
||||||
|
<li><a href="/orphanage" class="text-gray-300 hover:text-white transition-colors">Orphanage</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 class="font-semibold mb-4">Contact Info</h3>
|
||||||
|
<ul class="space-y-2 text-sm text-gray-300">
|
||||||
|
<li>📍 Amsterdam, Netherlands</li>
|
||||||
|
<li>📞 +31 (0) 123 456 789</li>
|
||||||
|
<li>📧 info@omoluabi.nl</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border-t border-gray-800 mt-8 pt-8 text-center text-sm text-gray-400">
|
||||||
|
<p>© 2024 Omoluabi Association Netherlands. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Scripts -->
|
||||||
|
<script>
|
||||||
|
// Mobile menu toggle
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
||||||
|
const mobileMenu = document.getElementById('mobile-menu');
|
||||||
|
|
||||||
|
if (mobileMenuButton && mobileMenu) {
|
||||||
|
mobileMenuButton.addEventListener('click', () => {
|
||||||
|
mobileMenu.classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersection Observer for scroll animations
|
||||||
|
const observerOptions = {
|
||||||
|
threshold: 0.1,
|
||||||
|
rootMargin: '0px 0px -50px 0px'
|
||||||
|
};
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('visible');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, observerOptions);
|
||||||
|
|
||||||
|
// Observe all elements with animation attributes
|
||||||
|
const animatedElements = document.querySelectorAll('[data-animate-on-scroll]');
|
||||||
|
animatedElements.forEach(el => observer.observe(el));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
<style>
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
body {
|
|
||||||
padding-top: 120px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,62 @@
|
|||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
import ContactForm from '../components/ContactForm.jsx';
|
import ContactForm from '../components/ContactForm.jsx';
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<h1 class="text-3xl font-headline font-bold text-primary mt-12 text-center">Contact Us</h1>
|
<!-- Page Header -->
|
||||||
<p class="mt-4 text-center">We'd love to hear from you! Please use the form below or reach out via our contact details.</p>
|
<section class="relative py-16 px-4 text-center text-white overflow-hidden bg-gradient-to-br from-nigerian-green-500 via-kente-gold-500 to-ankara-red-500" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="absolute inset-0 opacity-10" style="background-image: url('data:image/svg+xml,%3Csvg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cg fill="%23ffffff" fill-opacity="0.1"%3E%3Cpath d="M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E');"></div>
|
||||||
|
<div class="relative z-10 max-w-4xl mx-auto">
|
||||||
|
<div class="flex items-center justify-center gap-2 mb-4 text-sm">
|
||||||
|
<a href="/" class="text-white/80 hover:text-white transition-colors">🏠 Home</a>
|
||||||
|
<span class="text-white/60">•</span>
|
||||||
|
<span>Contact</span>
|
||||||
|
</div>
|
||||||
|
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-200 animate-text-shine">
|
||||||
|
Contact Us
|
||||||
|
</h1>
|
||||||
|
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">
|
||||||
|
We'd love to hear from you! Please use the form below or reach out via our contact details.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="container">
|
||||||
<ContactForm client:load />
|
<ContactForm client:load />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section bg-gray-50" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="container">
|
||||||
|
<div class="text-center mb-12">
|
||||||
|
<h2 class="font-headline text-4xl font-bold text-gray-900 mb-4">
|
||||||
|
Our <span class="text-nigerian-green-700">Contact</span> Details
|
||||||
|
</h2>
|
||||||
|
<p class="text-lg text-gray-600 max-w-2xl mx-auto">
|
||||||
|
Feel free to reach out to us through any of the following channels.
|
||||||
|
</p>
|
||||||
|
<div class="w-24 h-1 bg-gradient-to-r from-nigerian-green-500 to-kente-gold-500 mx-auto rounded-full mt-6"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 text-center">
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200" data-animate-on-scroll="slide-up">
|
||||||
|
<div class="text-4xl text-nigerian-green-500 mb-4">📍</div>
|
||||||
|
<h3 class="font-semibold text-xl text-gray-900 mb-2">Our Location</h3>
|
||||||
|
<p class="text-gray-700">Amsterdam, Netherlands</p>
|
||||||
|
<p class="text-gray-700">Various locations across NL</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200" data-animate-on-scroll="slide-up" style="transition-delay: 0.1s;">
|
||||||
|
<div class="text-4xl text-kente-gold-500 mb-4">📞</div>
|
||||||
|
<h3 class="font-semibold text-xl text-gray-900 mb-2">Phone Number</h3>
|
||||||
|
<p class="text-gray-700">+31 (0) 123 456 789</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200" data-animate-on-scroll="slide-up" style="transition-delay: 0.2s;">
|
||||||
|
<div class="text-4xl text-ankara-red-500 mb-4">📧</div>
|
||||||
|
<h3 class="font-semibold text-xl text-gray-900 mb-2">Email Address</h3>
|
||||||
|
<p class="text-gray-700">info@omoluabi.nl</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
|
import DonationProgressBar from '../components/DonationProgressBar.jsx';
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout>
|
||||||
|
<!-- Page Header -->
|
||||||
|
<section class="relative py-16 px-4 text-center text-white overflow-hidden bg-gradient-to-br from-nigerian-green-500 via-kente-gold-500 to-ankara-red-500" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="absolute inset-0 opacity-10" style="background-image: url('data:image/svg+xml,%3Csvg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cg fill="%23ffffff" fill-opacity="0.1"%3E%3Cpath d="M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E');"></div>
|
||||||
|
<div class="relative z-10 max-w-4xl mx-auto">
|
||||||
|
<div class="flex items-center justify-center gap-2 mb-4 text-sm">
|
||||||
|
<a href="/" class="text-white/80 hover:text-white transition-colors">🏠 Home</a>
|
||||||
|
<span class="text-white/60">•</span>
|
||||||
|
<span>Donate</span>
|
||||||
|
</div>
|
||||||
|
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-200 animate-text-shine">
|
||||||
|
Support Our Mission
|
||||||
|
</h1>
|
||||||
|
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">
|
||||||
|
Your generous contributions empower us to continue our work in preserving Nigerian culture, supporting our community, and making a difference in the lives of those in need.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="container">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-10 items-start">
|
||||||
|
<div data-animate-on-scroll="slide-up">
|
||||||
|
<h2 class="text-2xl font-headline font-bold text-nigerian-green-600 mb-4">Why Your Donation Matters</h2>
|
||||||
|
<p class="text-gray-700 leading-relaxed mb-4">
|
||||||
|
Every donation, no matter the size, directly impacts our programs and initiatives. From cultural events that bring our community together to vital support for our orphanage, your generosity fuels our mission.
|
||||||
|
</p>
|
||||||
|
<ul class="list-disc list-inside text-gray-700 space-y-2 mb-6">
|
||||||
|
<li>Preserving and promoting Nigerian cultural heritage.</li>
|
||||||
|
<li>Organizing community events and welfare programs.</li>
|
||||||
|
<li>Providing essential support to the orphanage.</li>
|
||||||
|
<li>Empowering Nigerian youth in the Netherlands.</li>
|
||||||
|
</ul>
|
||||||
|
<p class="text-gray-700 leading-relaxed">
|
||||||
|
We are committed to transparency and accountability. All funds are carefully managed and directed towards areas where they can make the most significant impact.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sticky top-24" data-animate-on-scroll="slide-up" style="transition-delay: 0.1s;">
|
||||||
|
<DonationProgressBar client:load />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section bg-gray-50" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="container">
|
||||||
|
<div class="text-center mb-12">
|
||||||
|
<h2 class="font-headline text-4xl font-bold text-kente-gold-700 mb-4">
|
||||||
|
Other Ways to Support
|
||||||
|
</h2>
|
||||||
|
<p class="text-lg text-gray-600 max-w-2xl mx-auto">
|
||||||
|
There are many ways you can contribute to our cause and make a difference.
|
||||||
|
</p>
|
||||||
|
<div class="w-24 h-1 bg-gradient-to-r from-kente-gold-500 to-nigerian-green-500 mx-auto rounded-full mt-6"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap justify-center gap-6">
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200 flex-1 min-w-[280px] max-w-[350px]" data-animate-on-scroll="slide-up">
|
||||||
|
<h3 class="text-xl font-semibold text-adire-blue-600 mb-3">Volunteer Your Time</h3>
|
||||||
|
<p class="text-gray-700 mb-4">
|
||||||
|
Lend a hand at our events or administrative tasks. Your skills are valuable!
|
||||||
|
</p>
|
||||||
|
<a href="/contact" class="text-adire-blue-500 hover:underline font-medium">Learn More →</a>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200 flex-1 min-w-[280px] max-w-[350px]" data-animate-on-scroll="slide-up" style="transition-delay: 0.1s;">
|
||||||
|
<h3 class="text-xl font-semibold text-ankara-red-600 mb-3">Become a Member</h3>
|
||||||
|
<p class="text-gray-700 mb-4">
|
||||||
|
Join our association and be part of a vibrant community.
|
||||||
|
</p>
|
||||||
|
<a href="/join" class="text-ankara-red-500 hover:underline font-medium">Join Us →</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</BaseLayout>
|
@@ -1,27 +1,33 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
|
import EventFilterSearch from '../components/EventFilterSearch.jsx';
|
||||||
|
|
||||||
const events = await Astro.glob('../../content/events/*.md');
|
const events = await Astro.glob('../../content/events/*.md');
|
||||||
const sortedEvents = events.sort((a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date));
|
const sortedEvents = events.sort((a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date));
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<h1 class="text-3xl font-headline font-bold text-primary mt-12 text-center">Events</h1>
|
<!-- Page Header -->
|
||||||
<p class="mt-4 text-center">See our upcoming and past events below.</p>
|
<section class="relative py-16 px-4 text-center text-white overflow-hidden bg-gradient-to-br from-nigerian-green-500 via-kente-gold-500 to-ankara-red-500" data-animate-on-scroll="fade-in">
|
||||||
<section class="max-w-5xl mx-auto mt-8 grid grid-cols-1 md:grid-cols-2 gap-8 px-4">
|
<div class="absolute inset-0 opacity-10" style="background-image: url('data:image/svg+xml,%3Csvg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cg fill="%23ffffff" fill-opacity="0.1"%3E%3Cpath d="M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E');"></div>
|
||||||
{sortedEvents.map(event => (
|
<div class="relative z-10 max-w-4xl mx-auto">
|
||||||
<article class="card bg-base-100 shadow-lg">
|
<div class="flex items-center justify-center gap-2 mb-4 text-sm">
|
||||||
<figure>
|
<a href="/" class="text-white/80 hover:text-white transition-colors">🏠 Home</a>
|
||||||
<img src={event.frontmatter.image} alt={event.frontmatter.title} class="w-full h-48 object-cover" />
|
<span class="text-white/60">•</span>
|
||||||
</figure>
|
<span>Events</span>
|
||||||
<div class="card-body">
|
|
||||||
<div class="flex items-center gap-2 mb-2">
|
|
||||||
<span class="badge badge-secondary">{event.frontmatter.category}</span>
|
|
||||||
<span class="text-xs text-gray-500">{new Date(event.frontmatter.date).toLocaleDateString()}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<h2 class="card-title text-lg font-bold">{event.frontmatter.title}</h2>
|
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-200 animate-text-shine">
|
||||||
<p class="text-sm mb-2">{event.frontmatter.description}</p>
|
Our Events
|
||||||
<div class="text-xs text-gray-400">{event.compiledContent()}</div>
|
</h1>
|
||||||
|
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">
|
||||||
|
Explore our upcoming and past events. Use the filters and search bar to find what're looking for!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="container">
|
||||||
|
<EventFilterSearch events={sortedEvents} client:load />
|
||||||
</div>
|
</div>
|
||||||
</article>
|
|
||||||
))}
|
|
||||||
</section>
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
18
src/pages/gallery.astro
Normal file
18
src/pages/gallery.astro
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
|
import ImageGallery from '../components/ImageGallery.jsx';
|
||||||
|
|
||||||
|
const galleryData = await Astro.glob('../../content/gallery/gallery-sample.md');
|
||||||
|
const images = galleryData[0]?.frontmatter.images || [];
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout>
|
||||||
|
<section class="max-w-6xl mx-auto py-12 px-4">
|
||||||
|
<h1 class="text-4xl font-headline font-bold text-center text-nigerian-green-700 mb-6">Our Photo Gallery</h1>
|
||||||
|
<p class="text-center text-lg text-gray-600 mb-10">
|
||||||
|
Explore moments of joy, culture, and community from our past events and activities.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ImageGallery images={images} client:load />
|
||||||
|
</section>
|
||||||
|
</BaseLayout>
|
File diff suppressed because it is too large
Load Diff
@@ -4,15 +4,34 @@ const membersData = await Astro.glob('../../content/members/members-sample.md');
|
|||||||
const intro = membersData[0]?.frontmatter.intro || '';
|
const intro = membersData[0]?.frontmatter.intro || '';
|
||||||
const members = membersData[0]?.frontmatter.members || [];
|
const members = membersData[0]?.frontmatter.members || [];
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<h1 class="text-3xl font-headline font-bold text-primary mt-12 text-center">Meet Our Members</h1>
|
<!-- Page Header -->
|
||||||
<section class="max-w-3xl mx-auto mt-8 mb-12 bg-base-100 rounded-xl shadow p-6">
|
<section class="relative py-16 px-4 text-center text-white overflow-hidden bg-gradient-to-br from-nigerian-green-500 via-kente-gold-500 to-ankara-red-500" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="absolute inset-0 opacity-10" style="background-image: url('data:image/svg+xml,%3Csvg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cg fill="%23ffffff" fill-opacity="0.1"%3E%3Cpath d="M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E');"></div>
|
||||||
|
<div class="relative z-10 max-w-4xl mx-auto">
|
||||||
|
<div class="flex items-center justify-center gap-2 mb-4 text-sm">
|
||||||
|
<a href="/" class="text-white/80 hover:text-white transition-colors">🏠 Home</a>
|
||||||
|
<span class="text-white/60">•</span>
|
||||||
|
<span>Members</span>
|
||||||
|
</div>
|
||||||
|
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-200 animate-text-shine">
|
||||||
|
Meet Our Members
|
||||||
|
</h1>
|
||||||
|
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">
|
||||||
|
Discover the dedicated individuals who form the heart of the Omoluabi Association.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="max-w-3xl mx-auto mt-8 mb-12 bg-base-100 rounded-xl shadow p-6" data-animate-on-scroll="fade-in">
|
||||||
<h2 class="text-xl font-bold mb-4 text-accent">Membership Benefits/Welfare Packages</h2>
|
<h2 class="text-xl font-bold mb-4 text-accent">Membership Benefits/Welfare Packages</h2>
|
||||||
<div class="text-gray-700 whitespace-pre-line">{intro}</div>
|
<div class="text-gray-700 whitespace-pre-line">{intro}</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="max-w-6xl mx-auto grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8 px-4 mb-20">
|
|
||||||
|
<section class="max-w-6xl mx-auto grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8 px-4 mb-20" data-animate-on-scroll="fade-in">
|
||||||
{members.map(member => (
|
{members.map(member => (
|
||||||
<div class="card bg-base-100 shadow-lg rounded-xl flex flex-col items-center p-4">
|
<div class="card bg-base-100 shadow-lg rounded-xl flex flex-col items-center p-4 transform transition-transform duration-300 hover:scale-105 hover:shadow-xl">
|
||||||
<img src={member.image} alt={member.name} class="w-40 h-40 object-cover rounded-lg mb-4" />
|
<img src={member.image} alt={member.name} class="w-40 h-40 object-cover rounded-lg mb-4" />
|
||||||
<h3 class="font-bold text-lg text-primary mb-1">{member.name}</h3>
|
<h3 class="font-bold text-lg text-primary mb-1">{member.name}</h3>
|
||||||
<p class="text-sm text-gray-600">{member.role}</p>
|
<p class="text-sm text-gray-600">{member.role}</p>
|
||||||
|
@@ -1,8 +1,94 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<h1 class="text-3xl font-headline font-bold text-nigerian-green mt-12 text-center">Orphanage</h1>
|
<!-- Page Header -->
|
||||||
<p class="mt-4 text-center">Learn more about our affiliated orphanage and how you can help.</p>
|
<section class="relative py-16 px-4 text-center text-white overflow-hidden bg-gradient-to-br from-nigerian-green-500 via-kente-gold-500 to-ankara-red-500" data-animate-on-scroll="fade-in">
|
||||||
<!-- Orphanage info, gallery, and donation details will go here -->
|
<div class="absolute inset-0 opacity-10" style="background-image: url('data:image/svg+xml,%3Csvg width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"%3E%3Cg fill="none" fill-rule="evenodd"%3E%3Cg fill="%23ffffff" fill-opacity="0.1"%3E%3Cpath d="M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E');"></div>
|
||||||
|
<div class="relative z-10 max-w-4xl mx-auto">
|
||||||
|
<div class="flex items-center justify-center gap-2 mb-4 text-sm">
|
||||||
|
<a href="/" class="text-white/80 hover:text-white transition-colors">🏠 Home</a>
|
||||||
|
<span class="text-white/60">•</span>
|
||||||
|
<span>Orphanage</span>
|
||||||
|
</div>
|
||||||
|
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-200 animate-text-shine">
|
||||||
|
Our Orphanage Program
|
||||||
|
</h1>
|
||||||
|
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">
|
||||||
|
Learn more about our affiliated orphanage and how you can help us provide a brighter future for children in need.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="container">
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||||
|
<div data-animate-on-scroll="slide-up">
|
||||||
|
<h2 class="font-headline text-3xl font-bold text-nigerian-green-700 mb-4">About the Orphanage</h2>
|
||||||
|
<p class="text-gray-700 leading-relaxed mb-4">
|
||||||
|
The Omoluabi Foundation is proud to support [Orphanage Name], a safe haven for orphaned and vulnerable children in Nigeria. Our partnership ensures that these children receive the care, education, and support they need to thrive.
|
||||||
|
</p>
|
||||||
|
<p class="text-gray-700 leading-relaxed mb-4">
|
||||||
|
At [Orphanage Name], children are provided with nutritious meals, comfortable shelter, access to quality education, and essential healthcare. Beyond basic needs, we strive to create a loving and nurturing environment where each child can develop their potential and build a hopeful future.
|
||||||
|
</p>
|
||||||
|
<ul class="list-disc list-inside text-gray-700 space-y-2">
|
||||||
|
<li>Providing safe and nurturing environment.</li>
|
||||||
|
<li>Ensuring access to quality education.</li>
|
||||||
|
<li>Offering healthcare and nutritional support.</li>
|
||||||
|
<li>Fostering personal growth and development.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="relative" data-animate-on-scroll="slide-up" style="transition-delay: 0.1s;">
|
||||||
|
<img src="/images/hero2.jpg" alt="Orphanage Children" class="w-full h-96 object-cover rounded-2xl shadow-xl" />
|
||||||
|
<div class="absolute -bottom-4 -left-4 bg-kente-gold-500 text-white px-4 py-2 rounded-full font-bold shadow-lg">Hope for the Future</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section bg-gray-50" data-animate-on-scroll="fade-in">
|
||||||
|
<div class="container">
|
||||||
|
<div class="text-center mb-12">
|
||||||
|
<h2 class="font-headline text-4xl font-bold text-gray-900 mb-4">
|
||||||
|
How You Can <span class="text-kente-gold-700">Help</span>
|
||||||
|
</h2>
|
||||||
|
<p class="text-lg text-gray-600 max-w-2xl mx-auto">
|
||||||
|
Your support makes a direct and meaningful impact on the lives of these children.
|
||||||
|
</p>
|
||||||
|
<div class="w-24 h-1 bg-gradient-to-r from-kente-gold-500 to-nigerian-green-500 mx-auto rounded-full mt-6"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200" data-animate-on-scroll="slide-up">
|
||||||
|
<h3 class="font-semibold text-xl text-nigerian-green-700 mb-3">Make a Donation</h3>
|
||||||
|
<p class="text-gray-700 mb-4">
|
||||||
|
Financial contributions are crucial for covering daily operational costs, food, education, and medical expenses.
|
||||||
|
</p>
|
||||||
|
<a href="/donate" class="text-nigerian-green-500 hover:underline font-medium">Donate Now →</a>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200" data-animate-on-scroll="slide-up" style="transition-delay: 0.1s;">
|
||||||
|
<h3 class="font-semibold text-xl text-kente-gold-700 mb-3">Sponsor a Child</h3>
|
||||||
|
<p class="text-gray-700 mb-4">
|
||||||
|
Provide ongoing support for a child's specific needs, including education and personal development.
|
||||||
|
</p>
|
||||||
|
<a href="/contact" class="text-kente-gold-500 hover:underline font-medium">Learn About Sponsorship →</a>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200" data-animate-on-scroll="slide-up" style="transition-delay: 0.2s;">
|
||||||
|
<h3 class="font-semibold text-xl text-ankara-red-700 mb-3">Volunteer Your Time</h3>
|
||||||
|
<p class="text-gray-700 mb-4">
|
||||||
|
If you are in Nigeria, consider volunteering directly at the orphanage to assist with daily activities.
|
||||||
|
</p>
|
||||||
|
<a href="/contact" class="text-ankara-red-500 hover:underline font-medium">Volunteer Today →</a>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white p-6 rounded-xl shadow-md border border-gray-200" data-animate-on-scroll="slide-up" style="transition-delay: 0.3s;">
|
||||||
|
<h3 class="font-semibold text-xl text-adire-blue-700 mb-3">Spread the Word</h3>
|
||||||
|
<p class="text-gray-700 mb-4">
|
||||||
|
Share our mission with your friends, family, and social networks to help us reach more supporters.
|
||||||
|
</p>
|
||||||
|
<a href="#" class="text-adire-blue-500 hover:underline font-medium">Share Now →</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</BaseLayout>
|
</BaseLayout>
|
@@ -1,26 +1,16 @@
|
|||||||
|
/* src/styles/global.css */
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Poppins:wght@400;500;600;700;800&family=Noto+Serif:wght@400;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Poppins:wght@400;500;600;700;800&family=Noto+Serif:wght@400;600;700&display=swap');
|
||||||
@import "tailwindcss/base";
|
|
||||||
@import "tailwindcss/components";
|
|
||||||
@import "tailwindcss/utilities";
|
|
||||||
|
|
||||||
/* Root Variables for Nigerian Theme */
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
/* CSS Variables */
|
||||||
:root {
|
:root {
|
||||||
--nigerian-green: #16a34a;
|
--nigerian-green: #16a34a;
|
||||||
--nigerian-white: #ffffff;
|
|
||||||
--kente-gold: #f59e0b;
|
--kente-gold: #f59e0b;
|
||||||
--adire-blue: #2563eb;
|
|
||||||
--ankara-red: #dc2626;
|
--ankara-red: #dc2626;
|
||||||
--earth-brown: #a18072;
|
--adire-blue: #2563eb;
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Inter', system-ui, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #fafafa 0%, #f0fdf4 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom scrollbar */
|
/* Custom scrollbar */
|
||||||
@@ -41,357 +31,138 @@ body {
|
|||||||
background: #15803d;
|
background: #15803d;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Smooth hover transitions */
|
/* Smooth scrolling */
|
||||||
* {
|
html {
|
||||||
transition: all 0.2s ease-in-out;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nigerian pattern overlay */
|
/* Animation keyframes */
|
||||||
.nigerian-pattern {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nigerian-pattern::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23f59e0b' fill-opacity='0.03'%3E%3Cpath d='M20 20c0 11.046-8.954 20-20 20v-40c11.046 0 20 8.954 20 20zM40 20c0 11.046-8.954 20-20 20v-40c11.046 0 20 8.954 20 20z'/%3E%3C/g%3E%3C/svg%3E");
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enhanced button styles */
|
|
||||||
.btn {
|
|
||||||
border-radius: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.025em;
|
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background: var(--nigerian-green);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
background: var(--kente-gold);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-accent {
|
|
||||||
background: var(--ankara-red);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: -100%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
|
||||||
transition: left 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover::before {
|
|
||||||
left: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 10px 25px -3px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-lg {
|
|
||||||
padding: 1rem 2rem;
|
|
||||||
font-size: 1.125rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Card enhancements */
|
|
||||||
.card {
|
|
||||||
border-radius: 16px;
|
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
border: 1px solid rgba(229, 229, 229, 0.8);
|
|
||||||
background: white;
|
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
transform: translateY(-4px);
|
|
||||||
box-shadow: 0 20px 40px -12px rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body {
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Input styling */
|
|
||||||
.input, .textarea {
|
|
||||||
border-radius: 12px;
|
|
||||||
border: 2px solid #e5e5e5;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
width: 100%;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input:focus, .textarea:focus {
|
|
||||||
border-color: var(--nigerian-green);
|
|
||||||
box-shadow: 0 0 0 3px rgba(22, 163, 74, 0.1);
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Badge styling */
|
|
||||||
.badge {
|
|
||||||
border-radius: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.025em;
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-primary {
|
|
||||||
background: var(--nigerian-green);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-secondary {
|
|
||||||
background: var(--kente-gold);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-lg {
|
|
||||||
padding: 0.75rem 1.25rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hero text animations */
|
|
||||||
@keyframes textShine {
|
@keyframes textShine {
|
||||||
0% { background-position: -200% center; }
|
0% { background-position: -200% center; }
|
||||||
100% { background-position: 200% center; }
|
100% { background-position: 200% center; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-shine {
|
|
||||||
background: linear-gradient(90deg, var(--nigerian-green), var(--kente-gold), var(--ankara-red), var(--nigerian-green));
|
|
||||||
background-size: 200% auto;
|
|
||||||
background-clip: text;
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
animation: textShine 3s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Animations */
|
|
||||||
@keyframes float {
|
@keyframes float {
|
||||||
0%, 100% { transform: translateY(0px); }
|
0%, 100% { transform: translateY(0px); }
|
||||||
50% { transform: translateY(-10px); }
|
50% { transform: translateY(-10px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideUp {
|
|
||||||
0% { transform: translateY(30px); opacity: 0; }
|
|
||||||
100% { transform: translateY(0px); opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
0% { opacity: 0; }
|
|
||||||
100% { opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes bounceGentle {
|
@keyframes bounceGentle {
|
||||||
0%, 100% { transform: translateY(0px); }
|
0%, 20%, 53%, 80%, 100% { transform: translateY(0); }
|
||||||
50% { transform: translateY(-5px); }
|
40%, 43% { transform: translateY(-10px); }
|
||||||
|
70% { transform: translateY(-5px); }
|
||||||
|
90% { transform: translateY(-2px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation classes */
|
||||||
|
.animate-text-shine {
|
||||||
|
background: linear-gradient(90deg, #ffffff, #e0e7ff, #8b5cf6, #e0e7ff, #ffffff);
|
||||||
|
background-size: 300% auto;
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
animation: textShine 8s linear infinite; /* Changed from 4s to 8s */
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-float {
|
.animate-float {
|
||||||
animation: float 6s ease-in-out infinite;
|
animation: float 6s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-slide-up {
|
|
||||||
animation: slideUp 0.8s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-fade-in {
|
|
||||||
animation: fadeIn 1s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-bounce-gentle {
|
.animate-bounce-gentle {
|
||||||
animation: bounceGentle 2s ease-in-out infinite;
|
animation: bounceGentle 2s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Color utilities */
|
.animation-delay-1s {
|
||||||
.text-nigerian-green { color: var(--nigerian-green); }
|
animation-delay: 1s;
|
||||||
.text-kente-gold { color: var(--kente-gold); }
|
|
||||||
.text-ankara-red { color: var(--ankara-red); }
|
|
||||||
.text-primary { color: var(--nigerian-green); }
|
|
||||||
.text-secondary { color: var(--kente-gold); }
|
|
||||||
.text-accent { color: var(--ankara-red); }
|
|
||||||
|
|
||||||
.bg-nigerian-green { background-color: var(--nigerian-green); }
|
|
||||||
.bg-kente-gold { background-color: var(--kente-gold); }
|
|
||||||
.bg-ankara-red { background-color: var(--ankara-red); }
|
|
||||||
.bg-primary { background-color: var(--nigerian-green); }
|
|
||||||
.bg-secondary { background-color: var(--kente-gold); }
|
|
||||||
.bg-accent { background-color: var(--ankara-red); }
|
|
||||||
|
|
||||||
/* Gradient backgrounds */
|
|
||||||
.bg-nigerian-gradient {
|
|
||||||
background: linear-gradient(135deg, var(--nigerian-green) 0%, var(--kente-gold) 100%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-cultural-gradient {
|
/* Layout classes */
|
||||||
background: linear-gradient(135deg, var(--nigerian-green) 0%, var(--kente-gold) 25%, var(--ankara-red) 75%, var(--nigerian-green) 100%);
|
@layer components {
|
||||||
|
.section {
|
||||||
|
@apply py-16;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Section padding utility */
|
|
||||||
.section-padding {
|
|
||||||
padding: 5rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.section-padding {
|
|
||||||
padding: 3rem 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Grid utilities */
|
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
@apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8;
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Typography */
|
/* Card components */
|
||||||
.font-headline { font-family: 'Poppins', system-ui, sans-serif; }
|
.card {
|
||||||
.font-body { font-family: 'Inter', system-ui, sans-serif; }
|
@apply bg-white rounded-xl shadow-lg overflow-hidden;
|
||||||
.font-cultural { font-family: 'Noto Serif', serif; }
|
|
||||||
|
|
||||||
/* Text sizes */
|
|
||||||
.text-xs { font-size: 0.75rem; }
|
|
||||||
.text-sm { font-size: 0.875rem; }
|
|
||||||
.text-base { font-size: 1rem; }
|
|
||||||
.text-lg { font-size: 1.125rem; }
|
|
||||||
.text-xl { font-size: 1.25rem; }
|
|
||||||
.text-2xl { font-size: 1.5rem; }
|
|
||||||
.text-3xl { font-size: 1.875rem; }
|
|
||||||
.text-4xl { font-size: 2.25rem; }
|
|
||||||
.text-5xl { font-size: 3rem; }
|
|
||||||
.text-6xl { font-size: 3.75rem; }
|
|
||||||
.text-7xl { font-size: 4.5rem; }
|
|
||||||
|
|
||||||
/* Font weights */
|
|
||||||
.font-light { font-weight: 300; }
|
|
||||||
.font-normal { font-weight: 400; }
|
|
||||||
.font-medium { font-weight: 500; }
|
|
||||||
.font-semibold { font-weight: 600; }
|
|
||||||
.font-bold { font-weight: 700; }
|
|
||||||
|
|
||||||
/* Spacing utilities */
|
|
||||||
.p-4 { padding: 1rem; }
|
|
||||||
.p-6 { padding: 1.5rem; }
|
|
||||||
.p-8 { padding: 2rem; }
|
|
||||||
.px-4 { padding-left: 1rem; padding-right: 1rem; }
|
|
||||||
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
|
|
||||||
.m-4 { margin: 1rem; }
|
|
||||||
.mb-4 { margin-bottom: 1rem; }
|
|
||||||
.mb-6 { margin-bottom: 1.5rem; }
|
|
||||||
.mb-8 { margin-bottom: 2rem; }
|
|
||||||
.mt-8 { margin-top: 2rem; }
|
|
||||||
.mt-12 { margin-top: 3rem; }
|
|
||||||
.mt-16 { margin-top: 4rem; }
|
|
||||||
.mt-20 { margin-top: 5rem; }
|
|
||||||
|
|
||||||
/* Layout utilities */
|
|
||||||
.flex { display: flex; }
|
|
||||||
.grid { display: grid; }
|
|
||||||
.block { display: block; }
|
|
||||||
.inline-flex { display: inline-flex; }
|
|
||||||
.items-center { align-items: center; }
|
|
||||||
.justify-center { justify-content: center; }
|
|
||||||
.justify-between { justify-content: space-between; }
|
|
||||||
.gap-4 { gap: 1rem; }
|
|
||||||
.gap-6 { gap: 1.5rem; }
|
|
||||||
.gap-8 { gap: 2rem; }
|
|
||||||
.gap-12 { gap: 3rem; }
|
|
||||||
|
|
||||||
/* Grid utilities */
|
|
||||||
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
|
|
||||||
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
||||||
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.md\\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
||||||
.md\\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1024px) {
|
.card-body {
|
||||||
.lg\\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
@apply p-6;
|
||||||
.lg\\:col-span-2 { grid-column: span 2 / span 2; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Positioning */
|
.card-title {
|
||||||
.relative { position: relative; }
|
@apply text-xl font-bold mb-2;
|
||||||
.absolute { position: absolute; }
|
}
|
||||||
.fixed { position: fixed; }
|
|
||||||
.top-0 { top: 0; }
|
|
||||||
.left-0 { left: 0; }
|
|
||||||
.right-0 { right: 0; }
|
|
||||||
.bottom-0 { bottom: 0; }
|
|
||||||
.inset-0 { top: 0; right: 0; bottom: 0; left: 0; }
|
|
||||||
.z-10 { z-index: 10; }
|
|
||||||
.z-50 { z-index: 50; }
|
|
||||||
|
|
||||||
/* Width and height */
|
/* Button components */
|
||||||
.w-full { width: 100%; }
|
.btn {
|
||||||
.h-full { height: 100%; }
|
@apply inline-flex items-center px-6 py-3 rounded-lg font-medium transition-all duration-200 cursor-pointer;
|
||||||
.min-h-screen { min-height: 100vh; }
|
}
|
||||||
.max-w-2xl { max-width: 42rem; }
|
|
||||||
.max-w-3xl { max-width: 48rem; }
|
|
||||||
.max-w-4xl { max-width: 56rem; }
|
|
||||||
.max-w-5xl { max-width: 64rem; }
|
|
||||||
.max-w-6xl { max-width: 72rem; }
|
|
||||||
.mx-auto { margin-left: auto; margin-right: auto; }
|
|
||||||
|
|
||||||
/* Rounded corners */
|
.btn-lg {
|
||||||
.rounded-lg { border-radius: 0.5rem; }
|
@apply px-8 py-4 text-lg;
|
||||||
.rounded-xl { border-radius: 0.75rem; }
|
}
|
||||||
.rounded-2xl { border-radius: 1rem; }
|
|
||||||
.rounded-full { border-radius: 9999px; }
|
|
||||||
|
|
||||||
/* Shadows */
|
/* Badge components */
|
||||||
.shadow-lg { box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); }
|
.badge {
|
||||||
.shadow-xl { box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); }
|
@apply inline-flex items-center px-3 py-1 rounded-full text-sm font-medium;
|
||||||
.shadow-2xl { box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); }
|
}
|
||||||
|
|
||||||
/* Transforms */
|
.badge-lg {
|
||||||
.transform { transform: translateX(var(--tw-translate-x, 0)) translateY(var(--tw-translate-y, 0)) rotate(var(--tw-rotate, 0)) skewX(var(--tw-skew-x, 0)) skewY(var(--tw-skew-y, 0)) scaleX(var(--tw-scale-x, 1)) scaleY(var(--tw-scale-y, 1)); }
|
@apply px-4 py-2;
|
||||||
.hover\\:scale-105:hover { --tw-scale-x: 1.05; --tw-scale-y: 1.05; }
|
}
|
||||||
.hover\\:-translate-y-2:hover { --tw-translate-y: -0.5rem; }
|
}
|
||||||
|
|
||||||
/* Text alignment */
|
/* Custom utility classes */
|
||||||
.text-center { text-align: center; }
|
@layer utilities {
|
||||||
.text-left { text-align: left; }
|
.text-shadow-lg {
|
||||||
|
text-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
/* Overflow */
|
.backdrop-blur-xs {
|
||||||
.overflow-hidden { overflow: hidden; }
|
backdrop-filter: blur(2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Background opacity */
|
/* Animation utilities */
|
||||||
.bg-opacity-80 { background-color: rgba(var(--tw-bg-opacity-value, 1), 0.8); }
|
[data-animate-on-scroll] {
|
||||||
.bg-opacity-90 { background-color: rgba(var(--tw-bg-opacity-value, 1), 0.9); }
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
transition: all 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
/* Object fit */
|
[data-animate-on-scroll].visible {
|
||||||
.object-cover { object-fit: cover; }
|
opacity: 1;
|
||||||
.object-contain { object-fit: contain; }
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive design adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.section {
|
||||||
|
@apply py-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title-shine {
|
||||||
|
font-size: 2.5rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduced motion support */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.animate-text-shine,
|
||||||
|
.animate-float,
|
||||||
|
.animate-bounce-gentle {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
}
|
||||||
|
}
|
3
tailwind-input.css
Normal file
3
tailwind-input.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
@@ -4,53 +4,36 @@ export default {
|
|||||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
'sans': ['Inter', 'system-ui', 'sans-serif'],
|
||||||
|
'headline': ['Poppins', 'system-ui', 'sans-serif'],
|
||||||
|
'serif': ['Noto Serif', 'serif'],
|
||||||
|
},
|
||||||
colors: {
|
colors: {
|
||||||
// Nigerian flag colors as primary palette
|
|
||||||
'nigerian-green': {
|
'nigerian-green': {
|
||||||
50: '#f0fdf4',
|
50: '#f0fdf4',
|
||||||
100: '#dcfce7',
|
100: '#dcfce7',
|
||||||
200: '#bbf7d0',
|
200: '#bbf7d0',
|
||||||
300: '#86efac',
|
300: '#86efac',
|
||||||
400: '#4ade80',
|
400: '#4ade80',
|
||||||
500: '#22c55e',
|
500: '#16a34a', // Primary Nigerian green
|
||||||
600: '#16a34a',
|
600: '#15803d',
|
||||||
700: '#15803d',
|
700: '#166534',
|
||||||
800: '#166534',
|
800: '#14532d',
|
||||||
900: '#14532d',
|
900: '#14532d',
|
||||||
},
|
},
|
||||||
'nigerian-white': {
|
|
||||||
50: '#ffffff',
|
|
||||||
100: '#fefefe',
|
|
||||||
200: '#fafafa',
|
|
||||||
300: '#f5f5f5',
|
|
||||||
400: '#efefef',
|
|
||||||
500: '#e5e5e5',
|
|
||||||
},
|
|
||||||
// Rich cultural colors inspired by Nigerian textiles
|
|
||||||
'kente-gold': {
|
'kente-gold': {
|
||||||
50: '#fffbeb',
|
50: '#fffbeb',
|
||||||
100: '#fef3c7',
|
100: '#fef3c7',
|
||||||
200: '#fde68a',
|
200: '#fde68a',
|
||||||
300: '#fcd34d',
|
300: '#fcd34d',
|
||||||
400: '#fbbf24',
|
400: '#fbbf24',
|
||||||
500: '#f59e0b',
|
500: '#f59e0b', // Primary gold
|
||||||
600: '#d97706',
|
600: '#d97706',
|
||||||
700: '#b45309',
|
700: '#b45309',
|
||||||
800: '#92400e',
|
800: '#92400e',
|
||||||
900: '#78350f',
|
900: '#78350f',
|
||||||
},
|
},
|
||||||
'adire-blue': {
|
|
||||||
50: '#eff6ff',
|
|
||||||
100: '#dbeafe',
|
|
||||||
200: '#bfdbfe',
|
|
||||||
300: '#93c5fd',
|
|
||||||
400: '#60a5fa',
|
|
||||||
500: '#3b82f6',
|
|
||||||
600: '#2563eb',
|
|
||||||
700: '#1d4ed8',
|
|
||||||
800: '#1e40af',
|
|
||||||
900: '#1e3a8a',
|
|
||||||
},
|
|
||||||
'ankara-red': {
|
'ankara-red': {
|
||||||
50: '#fef2f2',
|
50: '#fef2f2',
|
||||||
100: '#fee2e2',
|
100: '#fee2e2',
|
||||||
@@ -58,227 +41,46 @@ export default {
|
|||||||
300: '#fca5a5',
|
300: '#fca5a5',
|
||||||
400: '#f87171',
|
400: '#f87171',
|
||||||
500: '#ef4444',
|
500: '#ef4444',
|
||||||
600: '#dc2626',
|
600: '#dc2626', // Primary red
|
||||||
700: '#b91c1c',
|
700: '#b91c1c',
|
||||||
800: '#991b1b',
|
800: '#991b1b',
|
||||||
900: '#7f1d1d',
|
900: '#7f1d1d',
|
||||||
},
|
},
|
||||||
'earth-brown': {
|
'adire-blue': {
|
||||||
50: '#fdf8f6',
|
50: '#eff6ff',
|
||||||
100: '#f2e8e5',
|
100: '#dbeafe',
|
||||||
200: '#eaddd7',
|
200: '#bfdbfe',
|
||||||
300: '#e0cfc5',
|
300: '#93c5fd',
|
||||||
400: '#d2bab0',
|
400: '#60a5fa',
|
||||||
500: '#bfa094',
|
500: '#3b82f6',
|
||||||
600: '#a18072',
|
600: '#2563eb', // Primary blue
|
||||||
700: '#977669',
|
700: '#1d4ed8',
|
||||||
800: '#846358',
|
800: '#1e40af',
|
||||||
900: '#43302b',
|
900: '#1e3a8a',
|
||||||
}
|
|
||||||
},
|
},
|
||||||
fontFamily: {
|
|
||||||
'headline': ['Poppins', 'system-ui', 'sans-serif'],
|
|
||||||
'body': ['Inter', 'system-ui', 'sans-serif'],
|
|
||||||
'cultural': ['Noto Serif', 'serif'],
|
|
||||||
},
|
|
||||||
backgroundImage: {
|
|
||||||
'nigerian-pattern': `url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23f59e0b' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")`,
|
|
||||||
'kente-gradient': 'linear-gradient(135deg, #f59e0b 0%, #dc2626 25%, #16a34a 50%, #2563eb 75%, #f59e0b 100%)',
|
|
||||||
'hero-overlay': 'linear-gradient(135deg, rgba(22, 163, 74, 0.9) 0%, rgba(245, 158, 11, 0.8) 100%)',
|
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
|
'text-shine': 'text-shine 8s linear infinite', // Changed from 4s to 8s
|
||||||
'float': 'float 6s ease-in-out infinite',
|
'float': 'float 6s ease-in-out infinite',
|
||||||
'slide-up': 'slideUp 0.8s ease-out',
|
'bounce-gentle': 'bounce-gentle 2s infinite',
|
||||||
'fade-in': 'fadeIn 1s ease-out',
|
|
||||||
'bounce-gentle': 'bounceGentle 2s ease-in-out infinite',
|
|
||||||
},
|
},
|
||||||
keyframes: {
|
keyframes: {
|
||||||
float: {
|
'text-shine': {
|
||||||
|
'0%': { 'background-position': '-200% center' },
|
||||||
|
'100%': { 'background-position': '200% center' },
|
||||||
|
},
|
||||||
|
'float': {
|
||||||
'0%, 100%': { transform: 'translateY(0px)' },
|
'0%, 100%': { transform: 'translateY(0px)' },
|
||||||
'50%': { transform: 'translateY(-10px)' },
|
'50%': { transform: 'translateY(-10px)' },
|
||||||
},
|
},
|
||||||
slideUp: {
|
'bounce-gentle': {
|
||||||
'0%': { transform: 'translateY(30px)', opacity: '0' },
|
'0%, 20%, 53%, 80%, 100%': { transform: 'translateY(0)' },
|
||||||
'100%': { transform: 'translateY(0px)', opacity: '1' },
|
'40%, 43%': { transform: 'translateY(-10px)' },
|
||||||
},
|
'70%': { transform: 'translateY(-5px)' },
|
||||||
fadeIn: {
|
'90%': { transform: 'translateY(-2px)' },
|
||||||
'0%': { opacity: '0' },
|
|
||||||
'100%': { opacity: '1' },
|
|
||||||
},
|
|
||||||
bounceGentle: {
|
|
||||||
'0%, 100%': { transform: 'translateY(0px)' },
|
|
||||||
'50%': { transform: 'translateY(-5px)' },
|
|
||||||
}
|
|
||||||
},
|
|
||||||
boxShadow: {
|
|
||||||
'nigerian': '0 10px 25px -3px rgba(22, 163, 74, 0.1), 0 4px 6px -2px rgba(22, 163, 74, 0.05)',
|
|
||||||
'kente': '0 10px 25px -3px rgba(245, 158, 11, 0.2), 0 4px 6px -2px rgba(245, 158, 11, 0.1)',
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
daisyui: {
|
|
||||||
themes: [
|
|
||||||
{
|
|
||||||
omoluabi: {
|
|
||||||
"primary": "#16a34a", // Nigerian green
|
|
||||||
"secondary": "#f59e0b", // Kente gold
|
|
||||||
"accent": "#dc2626", // Ankara red
|
|
||||||
"neutral": "#fafafa", // Clean white base
|
|
||||||
"base-100": "#ffffff", // Pure white
|
|
||||||
"base-200": "#f5f5f5", // Light gray
|
|
||||||
"base-300": "#e5e5e5", // Medium gray
|
|
||||||
"info": "#2563eb", // Adire blue
|
|
||||||
"success": "#22c55e", // Success green
|
|
||||||
"warning": "#f59e0b", // Warning gold
|
|
||||||
"error": "#dc2626", // Error red
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
plugins: [],
|
||||||
},
|
|
||||||
plugins: [require("daisyui")],
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Updated global.css */
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Poppins:wght@400;500;600;700;800&family=Noto+Serif:wght@400;600;700&display=swap');
|
|
||||||
@import "tailwindcss";
|
|
||||||
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Inter', system-ui, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #fafafa 0%, #f0fdf4 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom scrollbar */
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
background: #f1f1f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background: #16a34a;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: #15803d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Smooth hover transitions */
|
|
||||||
* {
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Nigerian pattern overlay */
|
|
||||||
.nigerian-pattern {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nigerian-pattern::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg width='40' height='40' viewBox='0 0 40 40' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23f59e0b' fill-opacity='0.03'%3E%3Cpath d='M20 20c0 11.046-8.954 20-20 20v-40c11.046 0 20 8.954 20 20zM40 20c0 11.046-8.954 20-20 20v-40c11.046 0 20 8.954 20 20z'/%3E%3C/g%3E%3C/svg%3E");
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enhanced button styles */
|
|
||||||
.btn {
|
|
||||||
border-radius: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.025em;
|
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: -100%;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
|
||||||
transition: left 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover::before {
|
|
||||||
left: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 10px 25px -3px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Card enhancements */
|
|
||||||
.card {
|
|
||||||
border-radius: 16px;
|
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
border: 1px solid rgba(229, 229, 229, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover {
|
|
||||||
transform: translateY(-4px);
|
|
||||||
box-shadow: 0 20px 40px -12px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Input styling */
|
|
||||||
.input, .textarea {
|
|
||||||
border-radius: 12px;
|
|
||||||
border: 2px solid #e5e5e5;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input:focus, .textarea:focus {
|
|
||||||
border-color: #16a34a;
|
|
||||||
box-shadow: 0 0 0 3px rgba(22, 163, 74, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Badge styling */
|
|
||||||
.badge {
|
|
||||||
border-radius: 20px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: 0.025em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hero text animations */
|
|
||||||
@keyframes textShine {
|
|
||||||
0% { background-position: -200% center; }
|
|
||||||
100% { background-position: 200% center; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-shine {
|
|
||||||
background: linear-gradient(90deg, #16a34a, #f59e0b, #dc2626, #16a34a);
|
|
||||||
background-size: 200% auto;
|
|
||||||
background-clip: text;
|
|
||||||
-webkit-background-clip: text;
|
|
||||||
-webkit-text-fill-color: transparent;
|
|
||||||
animation: textShine 3s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loading animations */
|
|
||||||
.animate-pulse-slow {
|
|
||||||
animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom spacing utilities */
|
|
||||||
.section-padding {
|
|
||||||
padding: 5rem 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.section-padding {
|
|
||||||
padding: 3rem 1rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user