Compare commits
7 Commits
910253c8f4
...
99f92eaa7b
Author | SHA1 | Date | |
---|---|---|---|
99f92eaa7b | |||
0f86f8f615 | |||
725a3e8e3a | |||
ba9bb6dfe0 | |||
c8c550a00b | |||
a0ab66189a | |||
3c74d71e22 |
148
ACCESSIBILITY_AUDIT.md
Normal file
148
ACCESSIBILITY_AUDIT.md
Normal file
@@ -0,0 +1,148 @@
|
||||
### Omoluabi Accessibility Audit (WCAG 2.2 AA)
|
||||
|
||||
Scope: Current Astro site as of this commit. Focus on perceivable, operable, understandable, and robust criteria for Nigerians in NL and Dutch partners.
|
||||
|
||||
---
|
||||
|
||||
### Executive summary
|
||||
- **Overall**: Solid semantic base with clear landmarks in `src/layouts/BaseLayout.astro`. Reduced-motion support exists in `src/styles/global.css`. Key gaps: language/i18n markup, skip links, menu ARIA, carousel semantics, form error/ARIA, dialog semantics, and strict CSP readiness.
|
||||
- **Risk**: Medium → High for navigation/keyboard and language; Low → Medium for contrast on image/gradient backgrounds.
|
||||
|
||||
---
|
||||
|
||||
### High-priority issues (fix now)
|
||||
|
||||
1) Missing skip link + focus target
|
||||
- File: `src/layouts/BaseLayout.astro`
|
||||
- Snippet (add just inside `<body>` and set `id` on `<main>`):
|
||||
```astro
|
||||
<a href="#main" class="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-50 focus:bg-white focus:text-black focus:px-3 focus:py-2 focus:rounded">
|
||||
Skip to main content
|
||||
</a>
|
||||
...
|
||||
<main id="main" tabindex="-1">
|
||||
<slot />
|
||||
</main>
|
||||
```
|
||||
- Rationale: Allows keyboard users to bypass repeated navigation (WCAG 2.4.1, 2.4.3).
|
||||
|
||||
2) Page language + `hreflang`
|
||||
- File: `src/layouts/BaseLayout.astro`
|
||||
- Snippet (top `<html>` and `<head>`):
|
||||
```astro
|
||||
<html lang={Astro.props.lang ?? 'en'}>
|
||||
<head>
|
||||
...
|
||||
<link rel="alternate" hrefLang="en" href="/en/" />
|
||||
<link rel="alternate" hrefLang="nl" href="/nl/" />
|
||||
<link rel="alternate" hrefLang="x-default" href="/" />
|
||||
```
|
||||
- Rationale: Correct language for screen readers and localized SEO (WCAG 3.1.1, 3.1.2).
|
||||
|
||||
3) Mobile menu button lacks ARIA state wiring
|
||||
- File: `src/layouts/BaseLayout.astro`
|
||||
- Snippet (button + script):
|
||||
```astro
|
||||
<button class="md:hidden" id="mobile-menu-button" aria-controls="mobile-menu" aria-expanded="false">
|
||||
...
|
||||
</button>
|
||||
```
|
||||
```html
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
||||
const mobileMenu = document.getElementById('mobile-menu');
|
||||
if (mobileMenuButton && mobileMenu) {
|
||||
mobileMenuButton.addEventListener('click', () => {
|
||||
const isHidden = mobileMenu.classList.toggle('hidden');
|
||||
mobileMenuButton.setAttribute('aria-expanded', String(!isHidden));
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
- Rationale: Communicates expanded/collapsed state for assistive tech (WCAG 4.1.2).
|
||||
|
||||
4) Carousel semantics and controls
|
||||
- File: `src/components/HeroCarousel.jsx`
|
||||
- Snippet:
|
||||
```jsx
|
||||
<div
|
||||
className="relative w-full max-w-6xl mx-auto rounded-2xl overflow-hidden shadow-2xl bg-white"
|
||||
role="region"
|
||||
aria-roledescription="carousel"
|
||||
aria-label="Featured content"
|
||||
>
|
||||
{slides.map((slide, index) => (
|
||||
<div
|
||||
key={index}
|
||||
role="group"
|
||||
aria-roledescription="slide"
|
||||
aria-label={`${index + 1} of ${slides.length}`}
|
||||
className={...}
|
||||
>
|
||||
...
|
||||
</div>
|
||||
))}
|
||||
<button aria-pressed={!isPlaying} aria-label={isPlaying ? 'Pause slideshow' : 'Play slideshow'}>...</button>
|
||||
</div>
|
||||
```
|
||||
- Rationale: Conveys carousel/slide semantics; exposes play/pause state (WCAG 1.4.2, 2.2.2, 4.1.2).
|
||||
|
||||
5) Contact form ARIA and honeypot
|
||||
- File: `src/components/ContactForm.jsx`
|
||||
- Snippet:
|
||||
```jsx
|
||||
const [hp, setHp] = useState('');
|
||||
...
|
||||
<form onSubmit={handleSubmit} noValidate ...>
|
||||
<div className="hidden" aria-hidden="true">
|
||||
<label htmlFor="website">Website</label>
|
||||
<input id="website" name="website" value={hp} onChange={(e)=>setHp(e.target.value)} tabIndex={-1} autoComplete="off" />
|
||||
</div>
|
||||
<input aria-invalid={Boolean(errors.name)} aria-describedby={errors.name ? 'name-error' : undefined} ... />
|
||||
{errors.name && <p id="name-error" className="...">{errors.name}</p>}
|
||||
```
|
||||
- Rationale: Accessible errors association and simple anti-bot (WCAG 3.3.1/3.3.3).
|
||||
|
||||
6) Lightbox dialog semantics
|
||||
- File: `src/components/Lightbox.jsx`
|
||||
- Snippet:
|
||||
```jsx
|
||||
<div
|
||||
className="fixed inset-0 ..."
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Image viewer"
|
||||
onClick={onClose}
|
||||
>
|
||||
```
|
||||
- Rationale: Dialog semantics and modality (WCAG 1.3.1, 2.4.3, 4.1.2).
|
||||
|
||||
7) Inline styles and data-URI backgrounds limit strict CSP
|
||||
- Files: `src/pages/about.astro`, `src/pages/donate.astro`, `src/pages/orphanage.astro`
|
||||
- Fix: Extract inline `style="background: ..."` to named CSS classes in `src/styles/main.css` (e.g., `.bg-flag-nl-ng`, `.pattern-overlay`) and reference via `class` only. Move inline `<script>` in layout to a module file and import with `type="module"` and `nonce`.
|
||||
- Rationale: Enables strict non-`unsafe-inline` CSP (security and robustness).
|
||||
|
||||
---
|
||||
|
||||
### Medium-priority issues
|
||||
- **Contrast over imagery/gradients**: Ensure minimum overlay like `bg-black/60` behind text; avoid fully transparent gradient text for body copy. Files: `src/pages/index.astro`, `src/pages/about.astro`, `HeroCarousel.jsx`.
|
||||
- **Focus visibility baseline**: Add base `:focus-visible` style in `src/styles/global.css` to ensure consistent visible focus.
|
||||
- **Image dimensions**: Add `width`/`height` on `<img>` where known to prevent CLS.
|
||||
|
||||
---
|
||||
|
||||
### Suggested global utilities
|
||||
- File: `src/styles/global.css`
|
||||
```css
|
||||
:focus-visible { outline: 2px solid var(--nigerian-green); outline-offset: 2px; }
|
||||
[role="button"], a, button, input, select, textarea { -webkit-tap-highlight-color: transparent; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Verification plan (to add in CI)
|
||||
- Playwright + `@axe-core/playwright`: nav landmarks, skip link focus, keyboard-only menu, form labels/errors, dialog semantics.
|
||||
- Lighthouse budgets: LCP < 2.0s, CLS < 0.05, TTI < 3.5s; run on PR and fail if exceeded.
|
||||
- Spot-check color contrast with axe; prohibit text over images without overlay.
|
42
PRODUCT_BRIEF.md
Normal file
42
PRODUCT_BRIEF.md
Normal file
@@ -0,0 +1,42 @@
|
||||
## Product Brief — Omoluabi (NL)
|
||||
|
||||
### Audience
|
||||
- Nigerians living in the Netherlands (families, youth, professionals)
|
||||
- Dutch institutions and partners
|
||||
- Donors and volunteers
|
||||
|
||||
### Objectives
|
||||
- Community growth and engagement (events, programs, volunteering)
|
||||
- Cultural education and representation
|
||||
- Fundraising for programs and orphanage
|
||||
- Trust through accessibility, privacy, and transparency
|
||||
|
||||
### KPIs / Success metrics
|
||||
- Monthly active visitors (MAU) and returning visitors
|
||||
- Event registrations and attendance rate
|
||||
- Volunteer sign-ups and inquiries
|
||||
- Donations volume via iDEAL/cards (Mollie)
|
||||
- Newsletter subscriptions (double opt-in)
|
||||
- Accessibility: axe violations = 0 on core templates; Lighthouse: LCP < 2.0s, CLS < 0.05
|
||||
|
||||
### Value proposition
|
||||
A fast, inclusive, bilingual (EN/NL) hub connecting the Nigerian diaspora and Dutch partners, showcasing culture, programs, and ways to contribute—built privacy-first and accessible by default.
|
||||
|
||||
---
|
||||
|
||||
### Gap report (current site)
|
||||
|
||||
- **Internationalization**: No locale-aware routing or page `lang`; no language switcher. Impact: accessibility, SEO, Dutch partners.
|
||||
- **Accessibility**: Missing skip links; inconsistent focus; carousel lacks semantics; form errors lack ARIA; dialog lacks `role="dialog"`. Impact: keyboard and screen reader users.
|
||||
- **Security/Privacy**: Google Fonts import; inline scripts/styles block strict CSP; no headers/CSP; forms not server-validated; no anti-abuse. Impact: GDPR risk and security posture.
|
||||
- **Donations**: UI-only progress bar, no payments backend; no iDEAL/cards. Impact: fundraising.
|
||||
- **Analytics**: None; decision pending (Plausible/Matomo). Impact: insight and optimization.
|
||||
- **Content model**: Content lives in Markdown ad-hoc; lacks structured collections for posts/programs/team/partners; no Zod schemas.
|
||||
- **Images/Performance**: Static `<img>` without `width/height` and `<picture>`; risky contrast over images; potential CLS.
|
||||
|
||||
---
|
||||
|
||||
### Prioritized roadmap (Now/Next/Later)
|
||||
- **Now (this sprint)**: Implement headers/CSP-ready refactors, a11y fixes (skip link, menu ARIA, carousel/dialog semantics, focus baseline), self-host fonts, scaffold content collections with Zod, propose IA and i18n routing.
|
||||
- **Next**: Implement i18n routes (`/en/`, `/nl/`), language switcher, interactive events calendar with filters and ICS export, server forms (contact/volunteer/partner), Plausible EU.
|
||||
- **Later**: Mollie donation flow, Leaflet map for events/partners, PWA, program directory, newsletter double opt-in, Lighthouse/axe CI.
|
121
SECURITY_PRIVACY_AUDIT.md
Normal file
121
SECURITY_PRIVACY_AUDIT.md
Normal file
@@ -0,0 +1,121 @@
|
||||
### Security & Privacy Audit (EU/GDPR)
|
||||
|
||||
Scope: Current Astro site; no server endpoints yet; React islands used. Aim: strict security headers, EU analytics, consent only if needed, secrets hygiene, safe forms, and payment integration readiness.
|
||||
|
||||
---
|
||||
|
||||
### Executive summary
|
||||
- **Strengths**: Static-first Astro; no server auth; minimal third parties.
|
||||
- **Gaps**: External Google Fonts (tracking and CSP issues), inline scripts/styles, no security headers configured, forms lack server validation/rate-limiting/CSRF, no cookie/consent model, no analytics choice, donation flow not integrated with EU processor.
|
||||
|
||||
---
|
||||
|
||||
### Action checklist (priority)
|
||||
|
||||
1) Remove Google Fonts; self-host Inter variable
|
||||
- File: `src/styles/global.css`
|
||||
- Replace `@import` from Google with self-hosted font. Steps:
|
||||
- Add `public/fonts/inter/Inter-Variable.woff2`.
|
||||
- Add to `src/styles/global.css`:
|
||||
```css
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
src: url('/fonts/inter/Inter-Variable.woff2') format('woff2-variations');
|
||||
font-weight: 100 900; font-style: normal; font-display: swap;
|
||||
}
|
||||
```
|
||||
- Rationale: Privacy, performance, CSP friendliness.
|
||||
|
||||
2) Security headers
|
||||
- Option A: Static hosting `_headers` (e.g., Netlify/Vercel):
|
||||
```text
|
||||
/*
|
||||
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: strict-origin-when-cross-origin
|
||||
Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=()
|
||||
Cross-Origin-Opener-Policy: same-origin
|
||||
Cross-Origin-Embedder-Policy: require-corp; report-to="default"
|
||||
Cross-Origin-Resource-Policy: same-origin
|
||||
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-PLACEHOLDER'; style-src 'self' 'nonce-PLACEHOLDER'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
|
||||
```
|
||||
- Option B: Nginx snippet (`nginx.conf.example`):
|
||||
```nginx
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||
add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()";
|
||||
add_header Cross-Origin-Opener-Policy "same-origin";
|
||||
add_header Cross-Origin-Embedder-Policy "require-corp";
|
||||
add_header Cross-Origin-Resource-Policy "same-origin";
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$request_id'; style-src 'self' 'nonce-$request_id'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'";
|
||||
```
|
||||
- Rationale: Baseline browser protections; strict CSP with nonces for inline modules.
|
||||
|
||||
3) Inline → external scripts/styles to satisfy CSP
|
||||
- Files: `src/layouts/BaseLayout.astro` (inline `<script>`), several pages with inline `style="background..."`.
|
||||
- Fix: Move JS to `src/scripts/navigation.ts` and import with `<script type="module" src="/scripts/navigation.js" nonce={Astro.props.nonce} />`; extract inline styles into classes in `src/styles/main.css`.
|
||||
|
||||
4) Forms: server validation, CSRF, rate-limit, honeypot
|
||||
- Add endpoint example: `src/pages/api/contact.ts`
|
||||
```ts
|
||||
import type { APIRoute } from 'astro';
|
||||
import { z } from 'zod';
|
||||
|
||||
const ContactSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
email: z.string().email(),
|
||||
subject: z.string().min(1),
|
||||
message: z.string().min(1),
|
||||
website: z.string().max(0).optional(), // honeypot
|
||||
});
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
const data = await request.json();
|
||||
const parsed = ContactSchema.safeParse(data);
|
||||
if (!parsed.success) return new Response(JSON.stringify({ ok:false }), { status: 400 });
|
||||
// TODO: rate-limit via IP + UA (e.g., in-memory or KV), CSRF via double-submit token header
|
||||
// TODO: send via Postmark/Resend EU region
|
||||
return new Response(JSON.stringify({ ok:true }), { status: 200 });
|
||||
} catch {
|
||||
return new Response(JSON.stringify({ ok:false }), { status: 400 });
|
||||
}
|
||||
};
|
||||
```
|
||||
- Rationale: GDPR-friendly collection, safe error responses, anti-abuse measures.
|
||||
|
||||
5) Payments: Mollie integration plan
|
||||
- Use server-only handlers for payment creation and webhook signature verification. Store nothing beyond required payment IDs; no card data. Configure allowlisted endpoints in CSP `connect-src` for Mollie API only when enabled.
|
||||
|
||||
6) Analytics: Plausible (EU) or Matomo (self-host)
|
||||
- Recommendation: **Plausible EU** with cookieless, events limited to pageview + donate/volunteer conversions. If consent model needed later, implement prior to enabling cookies.
|
||||
|
||||
7) Secrets and environment hygiene
|
||||
- Use `import.meta.env` and never expose secrets without `*_PUBLIC` prefix. Add `.env.example`:
|
||||
```env
|
||||
PUBLIC_SITE_URL=
|
||||
NODE_ENV=development
|
||||
# Analytics
|
||||
PUBLIC_PLAUSIBLE_DOMAIN=
|
||||
PUBLIC_PLAUSIBLE_SRC=
|
||||
# Payments (server-only)
|
||||
MOLLIE_API_KEY=
|
||||
MOLLIE_WEBHOOK_SECRET=
|
||||
# Email provider
|
||||
POSTMARK_TOKEN=
|
||||
```
|
||||
|
||||
8) Logging and retention
|
||||
- Adopt structured logs without PII; error IDs returned to client; retain logs 30–90 days; no access logs with IP persisted beyond ops needs; document in Privacy Policy.
|
||||
|
||||
9) DPIA-lite
|
||||
- Data categories: contact form (identifiers and message body), newsletter (email), donation (processor-hosted, no card data). Lawful basis: consent/legitimate interest. No special category data processed. Document processor agreements (Plausible EU, Postmark EU region, Mollie EU).
|
||||
|
||||
---
|
||||
|
||||
### Next steps
|
||||
- Implement headers and CSP; refactor inline code.
|
||||
- Add API routes with Zod and anti-abuse; wire contact form to server.
|
||||
- Choose analytics (Plausible EU) and configure opt-in text in footer.
|
||||
- Prepare Mollie sandbox integration and webhook.
|
101
package-lock.json
generated
101
package-lock.json
generated
@@ -12,7 +12,7 @@
|
||||
"@tailwindcss/vite": "^4.1.10",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"astro": "^5.9.4",
|
||||
"astro": "^5.12.9",
|
||||
"keen-slider": "^6.8.6",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
@@ -54,18 +54,18 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@astrojs/internal-helpers": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.6.1.tgz",
|
||||
"integrity": "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.1.tgz",
|
||||
"integrity": "sha512-7dwEVigz9vUWDw3nRwLQ/yH/xYovlUA0ZD86xoeKEBmkz9O6iELG1yri67PgAPW6VLL/xInA4t7H0CK6VmtkKQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@astrojs/markdown-remark": {
|
||||
"version": "6.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.2.tgz",
|
||||
"integrity": "sha512-bO35JbWpVvyKRl7cmSJD822e8YA8ThR/YbUsciWNA7yTcqpIAL2hJDToWP5KcZBWxGT6IOdOkHSXARSNZc4l/Q==",
|
||||
"version": "6.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.5.tgz",
|
||||
"integrity": "sha512-MiR92CkE2BcyWf3b86cBBw/1dKiOH0qhLgXH2OXA6cScrrmmks1Rr4Tl0p/lFpvmgQQrP54Pd1uidJfmxGrpWQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/internal-helpers": "0.6.1",
|
||||
"@astrojs/internal-helpers": "0.7.1",
|
||||
"@astrojs/prism": "3.3.0",
|
||||
"github-slugger": "^2.0.0",
|
||||
"hast-util-from-html": "^2.0.3",
|
||||
@@ -80,7 +80,7 @@
|
||||
"remark-rehype": "^11.1.2",
|
||||
"remark-smartypants": "^3.0.2",
|
||||
"shiki": "^3.2.1",
|
||||
"smol-toml": "^1.3.1",
|
||||
"smol-toml": "^1.3.4",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-remove-position": "^5.0.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
@@ -1686,60 +1686,60 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@shikijs/core": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.6.0.tgz",
|
||||
"integrity": "sha512-9By7Xb3olEX0o6UeJyPLI1PE1scC4d3wcVepvtv2xbuN9/IThYN4Wcwh24rcFeASzPam11MCq8yQpwwzCgSBRw==",
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.9.2.tgz",
|
||||
"integrity": "sha512-3q/mzmw09B2B6PgFNeiaN8pkNOixWS726IHmJEpjDAcneDPMQmUg2cweT9cWXY4XcyQS3i6mOOUgQz9RRUP6HA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "3.6.0",
|
||||
"@shikijs/types": "3.9.2",
|
||||
"@shikijs/vscode-textmate": "^10.0.2",
|
||||
"@types/hast": "^3.0.4",
|
||||
"hast-util-to-html": "^9.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/engine-javascript": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.6.0.tgz",
|
||||
"integrity": "sha512-7YnLhZG/TU05IHMG14QaLvTW/9WiK8SEYafceccHUSXs2Qr5vJibUwsDfXDLmRi0zHdzsxrGKpSX6hnqe0k8nA==",
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.9.2.tgz",
|
||||
"integrity": "sha512-kUTRVKPsB/28H5Ko6qEsyudBiWEDLst+Sfi+hwr59E0GLHV0h8RfgbQU7fdN5Lt9A8R1ulRiZyTvAizkROjwDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "3.6.0",
|
||||
"@shikijs/types": "3.9.2",
|
||||
"@shikijs/vscode-textmate": "^10.0.2",
|
||||
"oniguruma-to-es": "^4.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/engine-oniguruma": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.6.0.tgz",
|
||||
"integrity": "sha512-nmOhIZ9yT3Grd+2plmW/d8+vZ2pcQmo/UnVwXMUXAKTXdi+LK0S08Ancrz5tQQPkxvjBalpMW2aKvwXfelauvA==",
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.9.2.tgz",
|
||||
"integrity": "sha512-Vn/w5oyQ6TUgTVDIC/BrpXwIlfK6V6kGWDVVz2eRkF2v13YoENUvaNwxMsQU/t6oCuZKzqp9vqtEtEzKl9VegA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "3.6.0",
|
||||
"@shikijs/types": "3.9.2",
|
||||
"@shikijs/vscode-textmate": "^10.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/langs": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.6.0.tgz",
|
||||
"integrity": "sha512-IdZkQJaLBu1LCYCwkr30hNuSDfllOT8RWYVZK1tD2J03DkiagYKRxj/pDSl8Didml3xxuyzUjgtioInwEQM/TA==",
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.9.2.tgz",
|
||||
"integrity": "sha512-X1Q6wRRQXY7HqAuX3I8WjMscjeGjqXCg/Sve7J2GWFORXkSrXud23UECqTBIdCSNKJioFtmUGJQNKtlMMZMn0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "3.6.0"
|
||||
"@shikijs/types": "3.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/themes": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.6.0.tgz",
|
||||
"integrity": "sha512-Fq2j4nWr1DF4drvmhqKq8x5vVQ27VncF8XZMBuHuQMZvUSS3NBgpqfwz/FoGe36+W6PvniZ1yDlg2d4kmYDU6w==",
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.9.2.tgz",
|
||||
"integrity": "sha512-6z5lBPBMRfLyyEsgf6uJDHPa6NAGVzFJqH4EAZ+03+7sedYir2yJBRu2uPZOKmj43GyhVHWHvyduLDAwJQfDjA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "3.6.0"
|
||||
"@shikijs/types": "3.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/types": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.6.0.tgz",
|
||||
"integrity": "sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg==",
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.9.2.tgz",
|
||||
"integrity": "sha512-/M5L0Uc2ljyn2jKvj4Yiah7ow/W+DJSglVafvWAJ/b8AZDeeRAdMu3c2riDzB7N42VD+jSnWxeP9AKtd4TfYVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/vscode-textmate": "^10.0.2",
|
||||
@@ -2341,14 +2341,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "5.9.4",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.9.4.tgz",
|
||||
"integrity": "sha512-AEulm16C9IijMYrFb3VIFx9z17p/wfDSHUHdbbvSEX+rBca64xV+f67tnsql3s4CE8u2cwYpdX+5yH7l53W4iA==",
|
||||
"version": "5.12.9",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.12.9.tgz",
|
||||
"integrity": "sha512-cZ7kZ61jyE5nwSrFKSRyf5Gds+uJELqQxJFqMkcgiWQvhWZJUSShn8Uz3yc9WLyLw5Kim5P5un9SkJSGogfEZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^2.12.2",
|
||||
"@astrojs/internal-helpers": "0.6.1",
|
||||
"@astrojs/markdown-remark": "6.3.2",
|
||||
"@astrojs/internal-helpers": "0.7.1",
|
||||
"@astrojs/markdown-remark": "6.3.5",
|
||||
"@astrojs/telemetry": "3.3.0",
|
||||
"@capsizecss/unpack": "^2.4.0",
|
||||
"@oslojs/encoding": "^1.1.0",
|
||||
@@ -2391,6 +2391,7 @@
|
||||
"rehype": "^13.0.2",
|
||||
"semver": "^7.7.1",
|
||||
"shiki": "^3.2.1",
|
||||
"smol-toml": "^1.3.4",
|
||||
"tinyexec": "^0.3.2",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"tsconfck": "^3.1.5",
|
||||
@@ -2404,7 +2405,7 @@
|
||||
"xxhash-wasm": "^1.1.0",
|
||||
"yargs-parser": "^21.1.1",
|
||||
"yocto-spinner": "^0.2.1",
|
||||
"zod": "^3.24.2",
|
||||
"zod": "^3.24.4",
|
||||
"zod-to-json-schema": "^3.24.5",
|
||||
"zod-to-ts": "^1.2.0"
|
||||
},
|
||||
@@ -6087,17 +6088,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/shiki": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-3.6.0.tgz",
|
||||
"integrity": "sha512-tKn/Y0MGBTffQoklaATXmTqDU02zx8NYBGQ+F6gy87/YjKbizcLd+Cybh/0ZtOBX9r1NEnAy/GTRDKtOsc1L9w==",
|
||||
"version": "3.9.2",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-3.9.2.tgz",
|
||||
"integrity": "sha512-t6NKl5e/zGTvw/IyftLcumolgOczhuroqwXngDeMqJ3h3EQiTY/7wmfgPlsmloD8oYfqkEDqxiaH37Pjm1zUhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/core": "3.6.0",
|
||||
"@shikijs/engine-javascript": "3.6.0",
|
||||
"@shikijs/engine-oniguruma": "3.6.0",
|
||||
"@shikijs/langs": "3.6.0",
|
||||
"@shikijs/themes": "3.6.0",
|
||||
"@shikijs/types": "3.6.0",
|
||||
"@shikijs/core": "3.9.2",
|
||||
"@shikijs/engine-javascript": "3.9.2",
|
||||
"@shikijs/engine-oniguruma": "3.9.2",
|
||||
"@shikijs/langs": "3.9.2",
|
||||
"@shikijs/themes": "3.9.2",
|
||||
"@shikijs/types": "3.9.2",
|
||||
"@shikijs/vscode-textmate": "^10.0.2",
|
||||
"@types/hast": "^3.0.4"
|
||||
}
|
||||
@@ -6131,9 +6132,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/smol-toml": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz",
|
||||
"integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.1.tgz",
|
||||
"integrity": "sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
|
@@ -13,7 +13,7 @@
|
||||
"@tailwindcss/vite": "^4.1.10",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"astro": "^5.9.4",
|
||||
"astro": "^5.12.9",
|
||||
"keen-slider": "^6.8.6",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
|
@@ -11,6 +11,7 @@ export default function ContactForm() {
|
||||
const [errors, setErrors] = useState({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [submitMessage, setSubmitMessage] = useState(null);
|
||||
const [hp, setHp] = useState('');
|
||||
|
||||
const validate = () => {
|
||||
let newErrors = {};
|
||||
@@ -61,7 +62,11 @@ export default function ContactForm() {
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="bg-white p-8 rounded-xl shadow-lg border border-gray-200 max-w-2xl mx-auto">
|
||||
<form onSubmit={handleSubmit} className="bg-white p-8 rounded-xl shadow-lg border border-gray-200 max-w-2xl mx-auto" noValidate>
|
||||
<div className="hidden" aria-hidden="true">
|
||||
<label htmlFor="website">Website</label>
|
||||
<input id="website" name="website" value={hp} onChange={(e)=>setHp(e.target.value)} tabIndex={-1} autoComplete="off" />
|
||||
</div>
|
||||
<h2 className="text-3xl font-bold text-center text-nigerian-green-700 mb-8">Send Us a Message</h2>
|
||||
|
||||
{submitMessage && (
|
||||
@@ -78,10 +83,12 @@ export default function ContactForm() {
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
aria-invalid={Boolean(errors.name)}
|
||||
aria-describedby={errors.name ? 'name-error' : undefined}
|
||||
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>}
|
||||
{errors.name && <p id="name-error" className="text-red-500 text-xs italic mt-2">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
<div className="mb-5">
|
||||
|
@@ -78,7 +78,8 @@ export default function HeroCarousel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-full max-w-6xl mx-auto rounded-2xl overflow-hidden shadow-2xl bg-white">
|
||||
<div className="relative w-full max-w-6xl mx-auto rounded-2xl overflow-hidden shadow-2xl bg-white"
|
||||
role="region" aria-roledescription="carousel" aria-label="Featured content">
|
||||
{/* Main Carousel Container */}
|
||||
<div className="relative h-[600px] overflow-hidden">
|
||||
{slides.map((slide, index) => {
|
||||
@@ -93,6 +94,9 @@ export default function HeroCarousel() {
|
||||
? 'opacity-0 -translate-x-full'
|
||||
: 'opacity-0 translate-x-full'
|
||||
}`}
|
||||
role="group"
|
||||
aria-roledescription="slide"
|
||||
aria-label={`${index + 1} of ${slides.length}`}
|
||||
>
|
||||
{/* Background Image */}
|
||||
<div className="absolute inset-0">
|
||||
@@ -132,10 +136,10 @@ export default function HeroCarousel() {
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<button className="bg-white text-gray-900 px-8 py-4 rounded-xl font-bold hover:bg-gray-100 transition-all duration-300 transform hover:scale-105 shadow-lg">
|
||||
<button className="bg-white dark:bg-gray-900 dark:text-white text-gray-900 px-8 py-4 rounded-xl font-bold hover:bg-gray-100 dark:hover:bg-gray-800 transition-all duration-300 transform hover:scale-105 shadow-lg">
|
||||
{slide.cta}
|
||||
</button>
|
||||
<button className="border-2 border-white text-white px-8 py-4 rounded-xl font-bold hover:bg-white hover:text-gray-900 transition-all duration-300 transform hover:scale-105">
|
||||
<button className="border-2 border-white text-white px-8 py-4 rounded-xl font-bold hover:bg-white hover:text-gray-900 dark:hover:text-gray-900 transition-all duration-300 transform hover:scale-105">
|
||||
Learn More
|
||||
</button>
|
||||
</div>
|
||||
@@ -194,6 +198,7 @@ export default function HeroCarousel() {
|
||||
onClick={() => setIsPlaying(!isPlaying)}
|
||||
className="absolute top-4 right-4 bg-white/20 backdrop-blur-sm hover:bg-white/30 text-white p-2 rounded-full transition-all duration-300 hover:scale-110"
|
||||
aria-label={isPlaying ? "Pause slideshow" : "Play slideshow"}
|
||||
aria-pressed={!isPlaying}
|
||||
>
|
||||
{isPlaying ? (
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
|
@@ -3,13 +3,13 @@ const gallery = await Astro.glob('../../content/gallery/gallery-sample.md');
|
||||
const images = gallery[0]?.frontmatter.images || [];
|
||||
---
|
||||
<section class="max-w-6xl mx-auto my-20 px-4">
|
||||
<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">
|
||||
<h2 class="text-3xl font-headline font-bold mb-6 text-nigerian-green-700 dark:text-kente-gold-400 text-center">Our Photo Gallery</h2>
|
||||
<p class="text-center text-lg text-gray-600 dark:text-gray-300 mb-10">
|
||||
A glimpse into our vibrant community and cultural celebrations.
|
||||
</p>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{images.slice(0, 4).map((img, idx) => (
|
||||
<div class="rounded-lg overflow-hidden shadow-md bg-base-100" key={idx}>
|
||||
{images.slice(0, 4).map((img: string, idx: number) => (
|
||||
<div class="rounded-lg overflow-hidden shadow-md bg-white dark:bg-gray-800">
|
||||
<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>
|
||||
))}
|
||||
|
@@ -21,7 +21,8 @@ export default function Lightbox({ images, currentIndex, 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="fixed inset-0 z-[9999] bg-black bg-opacity-90 flex items-center justify-center p-4" onClick={onClose}
|
||||
role="dialog" aria-modal="true" aria-label="Image viewer">
|
||||
<div className="relative max-w-5xl max-h-full" onClick={(e) => e.stopPropagation()}> {/* Prevent closing when clicking on image */}
|
||||
<img
|
||||
src={images[currentIndex]}
|
||||
|
42
src/components/ThemeToggle.jsx
Normal file
42
src/components/ThemeToggle.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function ThemeToggle() {
|
||||
const [isDark, setIsDark] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const stored = localStorage.getItem('omoluabi_theme');
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const initial = stored ? stored === 'dark' : prefersDark;
|
||||
setIsDark(initial);
|
||||
document.documentElement.classList.toggle('dark', initial);
|
||||
}, []);
|
||||
|
||||
const toggle = () => {
|
||||
const next = !isDark;
|
||||
setIsDark(next);
|
||||
document.documentElement.classList.toggle('dark', next);
|
||||
localStorage.setItem('omoluabi_theme', next ? 'dark' : 'light');
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={toggle}
|
||||
className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||||
aria-label="Toggle color theme"
|
||||
>
|
||||
{isDark ? (
|
||||
<>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24"><path d="M21.64 13a1 1 0 0 0-1.05-.14 8 8 0 1 1-9.45-9.45 1 1 0 0 0-.14-2A10 10 0 1 0 23 14a1 1 0 0 0-1.36-1z"/></svg>
|
||||
<span className="text-sm">Dark</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="currentColor" viewBox="0 0 24 24"><path d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.8 1.42-1.42zm10.45 14.32l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM12 4a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V5a1 1 0 0 1 1-1zm0 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm8-5h-2a1 1 0 1 1 0-2h2a1 1 0 1 1 0 2zM4 13H2a1 1 0 1 1 0-2h2a1 1 0 1 1 0 2zm2.76 6.36l-1.42 1.42-1.79-1.8 1.41-1.41 1.8 1.79zM19.78 4.46l-1.41-1.41-1.8 1.79 1.41 1.41 1.8-1.79z"/></svg>
|
||||
<span className="text-sm">Light</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,28 +1,37 @@
|
||||
---
|
||||
// src/layouts/BaseLayout.astro
|
||||
import '../styles/global.css';
|
||||
import '../styles/main.css';
|
||||
import ThemeToggle from '../components/ThemeToggle.jsx';
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
lang?: string;
|
||||
}
|
||||
|
||||
const { title = "Omoluabi Association Netherlands", description = "Preserving Nigerian culture and heritage in the Netherlands" } = Astro.props;
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang={Astro.props.lang ?? 'en'}>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{title}</title>
|
||||
<meta name="description" content={description} />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="alternate" hreflang="en" href="/en/" />
|
||||
<link rel="alternate" hreflang="nl" href="/nl/" />
|
||||
<link rel="alternate" hreflang="x-default" href="/" />
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-50">
|
||||
<body class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
||||
<a href="#main" class="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-50 focus:bg-white focus:text-black focus:px-3 focus:py-2 focus:rounded">
|
||||
Skip to main content
|
||||
</a>
|
||||
<!-- Navigation Header -->
|
||||
<header class="bg-white shadow-sm sticky top-0 z-50">
|
||||
<header class="bg-white dark:bg-gray-900/95 backdrop-blur shadow-md sticky top-0 z-50 border-b border-gray-200 dark:border-white/10">
|
||||
<nav class="container mx-auto px-4 py-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- Logo -->
|
||||
@@ -38,17 +47,20 @@ const { title = "Omoluabi Association Netherlands", description = "Preserving Ni
|
||||
|
||||
<!-- 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>
|
||||
<a href="/" class="text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400 transition-colors">Home</a>
|
||||
<a href="/about" class="text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400 transition-colors">About</a>
|
||||
<a href="/events" class="text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400 transition-colors">Events</a>
|
||||
<a href="/members" class="text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400 transition-colors">Members</a>
|
||||
<a href="/orphanage" class="text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400 transition-colors">Orphanage</a>
|
||||
<a href="/gallery" class="text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400 transition-colors">Gallery</a>
|
||||
<a href="/contact" class="text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400 transition-colors">Contact</a>
|
||||
</div>
|
||||
|
||||
<!-- CTA Buttons -->
|
||||
<!-- Right side controls -->
|
||||
<div class="hidden md:flex items-center space-x-4">
|
||||
<astro-island>
|
||||
<ThemeToggle client:load />
|
||||
</astro-island>
|
||||
<a href="/donate" class="btn bg-gradient-to-r from-ankara-red-500 to-kente-gold-500 text-white hover:shadow-lg">
|
||||
❤️ Donate
|
||||
</a>
|
||||
@@ -58,7 +70,7 @@ const { title = "Omoluabi Association Netherlands", description = "Preserving Ni
|
||||
</div>
|
||||
|
||||
<!-- Mobile menu button -->
|
||||
<button class="md:hidden" id="mobile-menu-button">
|
||||
<button class="md:hidden text-gray-900 dark:text-gray-200" id="mobile-menu-button" aria-controls="mobile-menu" aria-expanded="false">
|
||||
<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>
|
||||
@@ -67,14 +79,14 @@ const { title = "Omoluabi Association Netherlands", description = "Preserving Ni
|
||||
|
||||
<!-- 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="mt-2 rounded-lg bg-white dark:bg-gray-800 shadow-lg ring-1 ring-gray-900/5 dark:ring-white/10 pt-4 pb-2 space-y-2">
|
||||
<a href="/" class="block px-3 py-2 text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400">Home</a>
|
||||
<a href="/about" class="block px-3 py-2 text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400">About</a>
|
||||
<a href="/events" class="block px-3 py-2 text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400">Events</a>
|
||||
<a href="/members" class="block px-3 py-2 text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400">Members</a>
|
||||
<a href="/orphanage" class="block px-3 py-2 text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400">Orphanage</a>
|
||||
<a href="/gallery" class="block px-3 py-2 text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400">Gallery</a>
|
||||
<a href="/contact" class="block px-3 py-2 text-gray-900 dark:text-gray-200 hover:text-nigerian-green-700 dark:hover:text-kente-gold-400">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>
|
||||
@@ -85,7 +97,7 @@ const { title = "Omoluabi Association Netherlands", description = "Preserving Ni
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main>
|
||||
<main id="main" tabindex="-1">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
@@ -136,34 +148,22 @@ const { title = "Omoluabi Association Netherlands", description = "Preserving Ni
|
||||
|
||||
<!-- 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');
|
||||
const isHidden = mobileMenu.classList.toggle('hidden');
|
||||
mobileMenuButton.setAttribute('aria-expanded', String(!isHidden));
|
||||
});
|
||||
}
|
||||
|
||||
// Intersection Observer for scroll animations
|
||||
const observerOptions = {
|
||||
threshold: 0.1,
|
||||
rootMargin: '0px 0px -50px 0px'
|
||||
};
|
||||
|
||||
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');
|
||||
}
|
||||
});
|
||||
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));
|
||||
document.querySelectorAll('[data-animate-on-scroll]').forEach(el => observer.observe(el));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
@@ -3,204 +3,149 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
---
|
||||
|
||||
<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>
|
||||
<!-- Clean header -->
|
||||
<section class="brand-surface relative py-20 px-4 text-center text-white overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<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>
|
||||
<div class="flex items-center justify-center gap-2 mb-4 text-sm text-white/90">
|
||||
<a href="/" class="hover:underline">Home</a>
|
||||
<span class="opacity-60">/</span>
|
||||
<span>About</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">
|
||||
About Us
|
||||
</h1>
|
||||
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">
|
||||
Discover the rich heritage and noble mission of the Omoluabi Association in the Netherlands.
|
||||
|
||||
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight">About Us</h1>
|
||||
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto leading-relaxed">
|
||||
We connect <span class="font-semibold">Nigerian culture</span> with
|
||||
<span class="font-semibold">Dutch society</span>—supporting families
|
||||
and building opportunities across the Netherlands.
|
||||
</p>
|
||||
|
||||
<div class="mt-6 flex items-center justify-center gap-3">
|
||||
<span class="inline-flex items-center gap-2 text-sm bg-white/10 px-3 py-1 rounded-full border border-white/20">🇳🇬 Nigeria</span>
|
||||
<span class="inline-flex items-center gap-2 text-sm bg-white/10 px-3 py-1 rounded-full border border-white/20">🇳🇱 Netherlands</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Main About Section -->
|
||||
<section class="section" data-animate-on-scroll="fade-in">
|
||||
<section class="section bg-white dark:bg-gray-900" data-animate-on-scroll="fade-in">
|
||||
<div class="container">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||
<div class="bg-white p-8 rounded-2xl shadow-xl border border-gray-200 relative">
|
||||
<div class="absolute -inset-0.5 bg-gradient-to-br from-nigerian-green-500 via-kente-gold-500 to-ankara-red-500 rounded-2xl blur opacity-75 animate-pulse"></div>
|
||||
<div class="relative bg-white p-8 rounded-2xl">
|
||||
<h2 class="font-headline text-3xl md:text-4xl font-bold text-nigerian-green-700 mb-6 flex items-center gap-4">
|
||||
<div class="w-2 h-10 bg-gradient-to-b from-nigerian-green-500 to-kente-gold-500 rounded-full"></div>
|
||||
OMOLUABI ASSOCIATION IN THE NETHERLANDS (OAN)
|
||||
</h2>
|
||||
|
||||
<div class="text-gray-700 leading-relaxed text-lg">
|
||||
<p class="mb-4 font-medium">
|
||||
The <strong class="text-nigerian-green-600">Omoluabi Association</strong> in the Royal Kingdom of The Netherlands (OAN) is a pan-Yoruba Association with well-bred, selective, like-minded individuals of enviable character as our members.
|
||||
</p>
|
||||
|
||||
<p class="mb-4">
|
||||
It is worthy to note that our association is not solely focused on Yorubas or Nigerians only, but we extend our membership to culturally motivated Netherlanders who share our values and vision.
|
||||
</p>
|
||||
|
||||
<p class="mb-6">
|
||||
The OMOLUABI association of The Netherlands was established on <strong class="text-kente-gold-600">May 1st, 2018</strong> by well-bred, dignifying, and unique personalities that live across the Royal Kingdom of The Netherlands.
|
||||
</p>
|
||||
|
||||
<div class="bg-gradient-to-r from-nigerian-green-50 via-kente-gold-50 to-ankara-red-50 p-6 rounded-xl border-l-4 border-nigerian-green-500 shadow-inner">
|
||||
<p class="italic text-gray-800">
|
||||
"Our association is a <strong>non-profit, non-religious, non-partisan, non-ethnic</strong> and socio-cultural organization that focuses on showcasing the Yoruba cultural heritages, norms, values and traditions to the western world, most especially in Holland."
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 sm:grid-cols-4 gap-6 mt-8">
|
||||
<div class="text-center p-4 bg-gray-50 rounded-lg shadow-sm">
|
||||
<div class="text-2xl font-bold text-nigerian-green-600">2018</div>
|
||||
<div class="text-sm text-gray-600">Established</div>
|
||||
</div>
|
||||
<div class="text-center p-4 bg-gray-50 rounded-lg shadow-sm">
|
||||
<div class="text-2xl font-bold text-kente-gold-600">500+</div>
|
||||
<div class="text-sm text-gray-600">Members</div>
|
||||
</div>
|
||||
<div class="text-center p-4 bg-gray-50 rounded-lg shadow-sm">
|
||||
<div class="text-2xl font-bold text-ankara-red-600">50+</div>
|
||||
<div class="text-sm text-gray-600">Events Held</div>
|
||||
</div>
|
||||
<div class="text-center p-4 bg-gray-50 rounded-lg shadow-sm">
|
||||
<div class="text-2xl font-bold text-adire-blue-600">€50K+</div>
|
||||
<div class="text-sm text-gray-600">Raised for Charity</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative lg:pl-12">
|
||||
<div class="relative lg:order-2">
|
||||
<img src="/images/whoAreWe.webp" alt="Omoluabi Association Members" class="w-full h-96 object-cover rounded-2xl shadow-xl" />
|
||||
<div class="absolute -top-4 -right-4 bg-nigerian-green-500 text-white px-4 py-2 rounded-full font-bold shadow-lg animate-bounce-gentle">Est. 2018</div>
|
||||
</div>
|
||||
<div class="lg:order-1">
|
||||
<h2 class="font-headline text-3xl md:text-4xl font-bold text-gray-900 dark:text-white mb-4">Omoluabi Association in the Netherlands (OAN)</h2>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed text-lg mb-4">The <strong class="text-nigerian-green-600 dark:text-nigerian-green-400">Omoluabi Association</strong> is a pan‑Yoruba, community‑led organization welcoming everyone who shares our values.</p>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed text-lg mb-4">Founded on <strong class="text-kente-gold-600 dark:text-kente-gold-300">May 1, 2018</strong>, we preserve culture, support families, and build bridges with Dutch institutions.</p>
|
||||
<div class="grid grid-cols-2 sm:grid-cols-4 gap-4 mt-6">
|
||||
<div class="text-center p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"><div class="text-2xl font-bold text-nigerian-green-600">2018</div><div class="text-sm text-gray-600 dark:text-gray-300">Established</div></div>
|
||||
<div class="text-center p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"><div class="text-2xl font-bold text-kente-gold-600">500+</div><div class="text-sm text-gray-600 dark:text-gray-300">Members</div></div>
|
||||
<div class="text-center p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"><div class="text-2xl font-bold text-ankara-red-600">50+</div><div class="text-sm text-gray-600 dark:text-gray-300">Events</div></div>
|
||||
<div class="text-center p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"><div class="text-2xl font-bold text-adire-blue-600">€50K+</div><div class="text-sm text-gray-600 dark:text-gray-300">Raised</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- WHO ARE WE Section -->
|
||||
<section class="section bg-white" data-animate-on-scroll="fade-in">
|
||||
<section class="section bg-white dark:bg-gray-900" 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">
|
||||
<span class="text-nigerian-green-700">Who</span> Are We?
|
||||
</h2>
|
||||
<p class="text-lg text-gray-600 max-w-2xl mx-auto">
|
||||
Understanding the meaning and values behind the name "Omoluabi"
|
||||
</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>
|
||||
<h2 class="font-headline text-4xl font-bold text-gray-900 dark:text-white mb-3">Who are we?</h2>
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">The meaning and values behind the name “Omoluabi”.</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-gradient-to-r from-nigerian-green-50 via-kente-gold-50 to-ankara-red-50 p-8 rounded-2xl mb-12 shadow-inner">
|
||||
<div class="bg-gradient-to-r from-nigerian-green-50 via-kente-gold-50 to-ankara-red-50 dark:from-gray-800 dark:via-gray-800 dark:to-gray-800 p-8 rounded-2xl mb-12 shadow-inner">
|
||||
<div class="text-center mb-6">
|
||||
<h3 class="font-headline text-2xl font-bold text-nigerian-green-700 mb-4">
|
||||
<h3 class="font-headline text-2xl font-bold text-nigerian-green-700 dark:text-nigerian-green-400 mb-4">
|
||||
🌟 The Meaning of "Omoluabi"
|
||||
</h3>
|
||||
<p class="text-xl text-gray-800 leading-relaxed">
|
||||
Omoluabi is a Yoruba word that signifies <strong class="text-kente-gold-600">"WELL BRED INDIVIDUAL"</strong> (Omtrent het gedrag)
|
||||
<p class="text-xl text-gray-800 dark:text-gray-200 leading-relaxed">
|
||||
Omoluabi is a Yoruba word that signifies <strong class="text-kente-gold-600 dark:text-kente-gold-300">"WELL BRED INDIVIDUAL"</strong> (Omtrent het gedrag)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="text-gray-700 leading-relaxed text-lg text-center">
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed text-lg text-center">
|
||||
This choice of name was unilaterally agreed upon by all our members to bear as an association. The attributes associated with this dignifying name represent the core values we uphold and embody in our daily lives.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-headline text-2xl font-bold text-nigerian-green-700 mb-8 text-center">
|
||||
Our Fundamental Identities
|
||||
</h3>
|
||||
<h3 class="font-headline text-2xl font-bold text-gray-900 dark:text-white mb-6 text-center">Our values</h3>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div class="bg-nigerian-green-50 p-4 rounded-lg border-l-4 border-nigerian-green-500 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<strong class="text-gray-900">Ọ̀rọ̀ sísọ</strong> <span class="text-gray-600 text-sm">(Careful spoken words)</span>
|
||||
</div>
|
||||
<div class="bg-kente-gold-50 p-4 rounded-lg border-l-4 border-kente-gold-500 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<strong class="text-gray-900">Ìtẹríba</strong> <span class="text-gray-600 text-sm">(Respect)</span>
|
||||
</div>
|
||||
<div class="bg-ankara-red-50 p-4 rounded-lg border-l-4 border-ankara-red-500 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<strong class="text-gray-900">Irera eni sile</strong> <span class="text-gray-600 text-sm">(Humility)</span>
|
||||
</div>
|
||||
<div class="bg-adire-blue-50 p-4 rounded-lg border-l-4 border-adire-blue-500 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<strong class="text-gray-900">Inú rere</strong> <span class="text-gray-600 text-sm">(Think good about others)</span>
|
||||
</div>
|
||||
<div class="bg-nigerian-green-50 p-4 rounded-lg border-l-4 border-nigerian-green-500 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<strong class="text-gray-900">Òtítọ́</strong> <span class="text-gray-600 text-sm">(Truth)</span>
|
||||
</div>
|
||||
<div class="bg-kente-gold-50 p-4 rounded-lg border-l-4 border-kente-gold-500 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<strong class="text-gray-900">Ìwà</strong> <span class="text-gray-600 text-sm">(Good character)</span>
|
||||
</div>
|
||||
<div class="bg-ankara-red-50 p-4 rounded-lg border-l-4 border-ankara-red-500 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<strong class="text-gray-900">Akínkanjú</strong> <span class="text-gray-600 text-sm">(Bravery)</span>
|
||||
</div>
|
||||
<div class="bg-adire-blue-50 p-4 rounded-lg border-l-4 border-adire-blue-500 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<strong class="text-gray-900">Isẹ́ sísẹ</strong> <span class="text-gray-600 text-sm">(Hard working)</span>
|
||||
</div>
|
||||
<div class="bg-nigerian-green-50 p-4 rounded-lg border-l-4 border-nigerian-green-500 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<strong class="text-gray-900">Ọpọlọ pípé</strong> <span class="text-gray-600 text-sm">(Highly intelligent)</span>
|
||||
{[
|
||||
['Ọ̀rọ̀ sísọ','Careful spoken words'],
|
||||
['Ìtẹríba','Respect'],
|
||||
['Irera eni sile','Humility'],
|
||||
['Inú rere','Think good about others'],
|
||||
['Òtítọ́','Truth'],
|
||||
['Ìwà','Good character'],
|
||||
['Akínkanjú','Bravery'],
|
||||
['Isẹ́ sísẹ','Hard working'],
|
||||
['Ọpọlọ pípé','Highly intelligent']
|
||||
].map(([title,subtitle]) => (
|
||||
<div class="p-4 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-sm">
|
||||
<div class="font-semibold text-gray-900 dark:text-white">{title}</div>
|
||||
<div class="text-gray-600 dark:text-gray-300 text-sm">({subtitle})</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Mission & Vision Section -->
|
||||
<section class="section bg-gray-50" data-animate-on-scroll="fade-in">
|
||||
<section class="section bg-gray-50 dark:bg-gray-900" 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">Mission</span> & <span class="text-kente-gold-700">Vision</span>
|
||||
</h2>
|
||||
<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>
|
||||
<h2 class="font-headline text-4xl font-bold text-gray-900 dark:text-white mb-3">Mission & Vision</h2>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<div class="bg-white p-6 rounded-xl shadow-md border-l-4 border-nigerian-green-500 hover:shadow-lg transition-all duration-200">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border-l-4 border-nigerian-green-500 hover:shadow-lg transition-all duration-200">
|
||||
<span class="text-3xl mb-3 block">🎯</span>
|
||||
<h3 class="font-semibold text-xl text-gray-900 mb-2">Motto</h3>
|
||||
<p class="text-gray-700 leading-relaxed">
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-white mb-2">Motto</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
To promote, enhance and exhibit the Yoruba cultural heritage globally with a starting point in The Netherlands.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-md border-l-4 border-kente-gold-500 hover:shadow-lg transition-all duration-200">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border-l-4 border-kente-gold-500 hover:shadow-lg transition-all duration-200">
|
||||
<span class="text-3xl mb-3 block">🤝</span>
|
||||
<h3 class="font-semibold text-xl text-gray-900 mb-2">Aims and Objectives</h3>
|
||||
<p class="text-gray-700 leading-relaxed">
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-white mb-2">Aims and Objectives</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
To promote and defend our traditions, values, norms and culture in the western world.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-md border-l-4 border-ankara-red-500 hover:shadow-lg transition-all duration-200">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border-l-4 border-ankara-red-500 hover:shadow-lg transition-all duration-200">
|
||||
<span class="text-3xl mb-3 block">❤️</span>
|
||||
<h3 class="font-semibold text-xl text-gray-900 mb-2">Our Mission</h3>
|
||||
<p class="text-gray-700 leading-relaxed">
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-white mb-2">Our Mission</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
To cherish, uphold, and project the honor and dignity of Yoruba culture, language and tradition in Africa and Diaspora.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-md border-l-4 border-adire-blue-500 hover:shadow-lg transition-all duration-200">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border-l-4 border-adire-blue-500 hover:shadow-lg transition-all duration-200">
|
||||
<span class="text-3xl mb-3 block">💡</span>
|
||||
<h3 class="font-semibold text-xl text-gray-900 mb-2">Our Vision</h3>
|
||||
<p class="text-gray-700 leading-relaxed">
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-white mb-2">Our Vision</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
To harmonize all resources and strength of our people towards development (cultural, social and economical).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-md border-l-4 border-nigerian-green-500 hover:shadow-lg transition-all duration-200">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border-l-4 border-nigerian-green-500 hover:shadow-lg transition-all duration-200">
|
||||
<span class="text-3xl mb-3 block">🎭</span>
|
||||
<h3 class="font-semibold text-xl text-gray-900 mb-2">Activities</h3>
|
||||
<p class="text-gray-700 leading-relaxed">
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-white mb-2">Activities</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
Promoting cultural heritage, cultural development of Yoruba land, and teaching our children local languages (Yoruba).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-xl shadow-md border-l-4 border-kente-gold-500 hover:shadow-lg transition-all duration-200">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border-l-4 border-kente-gold-500 hover:shadow-lg transition-all duration-200">
|
||||
<span class="text-3xl mb-3 block">🗣️</span>
|
||||
<h3 class="font-semibold text-xl text-gray-900 mb-2">Slogan</h3>
|
||||
<p class="text-gray-700 leading-relaxed">
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-white mb-2">Slogan</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
"ASA WAA! TIWA N TIWA A WA GAN GAN LAWA, WON KO NI FI ELO MIRAN SE WA"
|
||||
</p>
|
||||
</div>
|
||||
@@ -283,13 +228,13 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section class="section bg-gradient-to-br from-nigerian-green-500 via-kente-gold-500 to-ankara-red-500 text-white text-center relative overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<section class="section brand-surface text-white text-center relative overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<div class="absolute top-10 left-10 w-32 h-32 bg-white/10 rounded-full animate-float"></div>
|
||||
<div class="absolute bottom-10 right-10 w-24 h-24 bg-white/10 rounded-full animate-float animation-delay-1s"></div>
|
||||
|
||||
<div class="container relative z-10">
|
||||
<h2 class="font-headline text-4xl md:text-5xl font-bold mb-6 leading-tight">
|
||||
Ready to Join Our <span class="bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-200">Community</span>?
|
||||
Ready to join our community?
|
||||
</h2>
|
||||
|
||||
<p class="text-lg md:text-xl opacity-90 max-w-3xl mx-auto mb-8">
|
||||
|
@@ -4,58 +4,53 @@ import ContactForm from '../components/ContactForm.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>
|
||||
<!-- Page Header (modern) -->
|
||||
<section class="brand-surface relative py-20 px-4 text-center text-white overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<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>
|
||||
<div class="flex items-center justify-center gap-2 mb-4 text-sm text-white/90">
|
||||
<a href="/" class="hover:underline">Home</a>
|
||||
<span class="opacity-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>
|
||||
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight">Contact Us</h1>
|
||||
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">We’d love to hear from you. Use the form or our details below.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" data-animate-on-scroll="fade-in">
|
||||
<section class="section bg-white dark:bg-gray-900" data-animate-on-scroll="fade-in">
|
||||
<div class="container">
|
||||
<ContactForm client:load />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section bg-gray-50" data-animate-on-scroll="fade-in">
|
||||
<section class="section bg-gray-50 dark:bg-gray-900" 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">
|
||||
<h2 class="font-headline text-4xl font-bold text-gray-900 dark:text-white 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">
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300 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="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border border-gray-200 dark:border-gray-700" 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>
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-white mb-2">Our Location</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300">Amsterdam, Netherlands</p>
|
||||
<p class="text-gray-700 dark:text-gray-300">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="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border border-gray-200 dark:border-gray-700" 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>
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-white mb-2">Phone Number</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300">+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="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border border-gray-200 dark:border-gray-700" 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>
|
||||
<h3 class="font-semibold text-xl text-gray-900 dark:text-white mb-2">Email Address</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300">info@omoluabi.nl</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -5,13 +5,14 @@ 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">
|
||||
<section class="relative py-16 px-4 text-center text-white overflow-hidden nigerian-flag-bg" 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>
|
||||
<span class="ml-4"><span class="dutch-flag-badge"><span class="stripe red"></span><span class="stripe white"></span><span class="stripe blue"></span></span></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
|
||||
|
@@ -4,29 +4,24 @@ import EventFilterSearch from '../components/EventFilterSearch.jsx';
|
||||
|
||||
const eventFiles = Object.values(await import.meta.glob('../../content/events/*.md', { eager: true })) as any[];
|
||||
const events = eventFiles;
|
||||
const sortedEvents = events.sort((a, b) => new Date(b.frontmatter?.date || 0) - new Date(a.frontmatter?.date || 0));
|
||||
const sortedEvents = events.sort((a, b) => new Date(b.frontmatter?.date || 0).getTime() - new Date(a.frontmatter?.date || 0).getTime());
|
||||
---
|
||||
|
||||
<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>
|
||||
<!-- Page Header (modern) -->
|
||||
<section class="brand-surface relative py-20 px-4 text-center text-white overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<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>
|
||||
<div class="flex items-center justify-center gap-2 mb-4 text-sm text-white/90">
|
||||
<a href="/" class="hover:underline">Home</a>
|
||||
<span class="opacity-60">/</span>
|
||||
<span>Events</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 Events
|
||||
</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 you're looking for!
|
||||
</p>
|
||||
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight">Events</h1>
|
||||
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">Find community gatherings, cultural celebrations, and programs across the Netherlands.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" data-animate-on-scroll="fade-in">
|
||||
<section class="section bg-white dark:bg-gray-900" data-animate-on-scroll="fade-in">
|
||||
<div class="container">
|
||||
<EventFilterSearch events={sortedEvents} client:load />
|
||||
</div>
|
||||
|
@@ -7,11 +7,13 @@ const images = galleryFiles[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>
|
||||
<section class="brand-surface relative py-20 px-4 text-center text-white overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<div class="relative z-10 max-w-4xl mx-auto">
|
||||
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight">Gallery</h1>
|
||||
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">Moments of culture, community, and celebration.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="max-w-6xl mx-auto py-12 px-4 bg-white dark:bg-gray-900">
|
||||
<ImageGallery images={images} client:load />
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
@@ -12,27 +12,27 @@ import HomeGallery from '../components/HomeGallery.astro';
|
||||
<div class="container">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 items-center">
|
||||
<div class="relative">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-kente-gold-500 to-ankara-red-500 rounded-2xl transform rotate-2"></div>
|
||||
<div class="relative bg-white p-2 rounded-2xl shadow-xl transform -rotate-1">
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-kente-gold-500 to-ankara-red-500 rounded-2xl transform rotate-2 opacity-90 dark:opacity-60"></div>
|
||||
<div class="relative bg-white dark:bg-gray-800 p-2 rounded-2xl shadow-xl transform -rotate-1">
|
||||
<img src="/images/whoAreWe.webp" alt="About Omoluabi Association" class="w-full h-80 object-cover rounded-xl" />
|
||||
<div class="absolute -top-4 -right-4 bg-nigerian-green-500 text-white px-4 py-2 rounded-full font-bold shadow-lg animate-bounce-gentle">Est. 2018</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2 class="font-headline text-4xl font-bold text-nigerian-green-700 mb-4 flex items-center gap-3">
|
||||
<h2 class="font-headline text-4xl font-bold text-nigerian-green-700 dark:text-kente-gold-400 mb-4 flex items-center gap-3">
|
||||
<div class="w-2 h-10 bg-gradient-to-b from-nigerian-green-500 to-kente-gold-500 rounded-full"></div>
|
||||
About Us
|
||||
</h2>
|
||||
|
||||
<h3 class="text-2xl font-semibold text-kente-gold-600 mb-4 flex items-center gap-2">
|
||||
<h3 class="text-2xl font-semibold text-kente-gold-600 dark:text-kente-gold-400 mb-4 flex items-center gap-2">
|
||||
<span class="text-3xl">🌟</span>
|
||||
WHO ARE WE?
|
||||
</h3>
|
||||
|
||||
<div class="text-gray-700 leading-relaxed text-lg mb-6">
|
||||
<div class="text-gray-700 dark:text-gray-200 leading-relaxed text-lg mb-6">
|
||||
<p class="mb-4">
|
||||
The <strong class="text-nigerian-green-600">Omoluabi Association</strong> in the Royal Kingdom of The Netherlands (OAN) is a pan-Yoruba Association with well-bred, selective, like-minded individuals of enviable character as our members.
|
||||
The <strong class="text-nigerian-green-600 dark:text-nigerian-green-400">Omoluabi Association</strong> in the Royal Kingdom of The Netherlands (OAN) is a pan-Yoruba Association with well-bred, selective, like-minded individuals of enviable character as our members.
|
||||
</p>
|
||||
<p class="mb-4">
|
||||
Our association extends beyond Yorubas or Nigerians only – we welcome culturally motivated Netherlanders who share our values and vision for community building.
|
||||
@@ -43,25 +43,25 @@ import HomeGallery from '../components/HomeGallery.astro';
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6">
|
||||
<div class="bg-white p-4 rounded-lg shadow-md text-center">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md text-center">
|
||||
<div class="text-2xl font-bold text-nigerian-green-600">2018</div>
|
||||
<div class="text-sm text-gray-600">Established</div>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-300">Established</div>
|
||||
</div>
|
||||
<div class="bg-white p-4 rounded-lg shadow-md text-center">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md text-center">
|
||||
<div class="text-2xl font-bold text-kente-gold-600">500+</div>
|
||||
<div class="text-sm text-gray-600">Members</div>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-300">Members</div>
|
||||
</div>
|
||||
<div class="bg-white p-4 rounded-lg shadow-md text-center">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md text-center">
|
||||
<div class="text-2xl font-bold text-ankara-red-600">50+</div>
|
||||
<div class="text-sm text-gray-600">Events</div>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-300">Events</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="badge badge-lg bg-nigerian-green-100 text-nigerian-green-700">Non-Profit</span>
|
||||
<span class="badge badge-lg bg-kente-gold-100 text-kente-gold-700">Cultural Heritage</span>
|
||||
<span class="badge badge-lg bg-ankara-red-100 text-ankara-red-700">Community Focus</span>
|
||||
<span class="badge badge-lg bg-adire-blue-100 text-adire-blue-700">Inclusive</span>
|
||||
<span class="badge badge-lg bg-nigerian-green-100 dark:bg-nigerian-green-600/20 text-nigerian-green-700 dark:text-nigerian-green-300">Non-Profit</span>
|
||||
<span class="badge badge-lg bg-kente-gold-100 dark:bg-kente-gold-600/20 text-kente-gold-700 dark:text-kente-gold-300">Cultural Heritage</span>
|
||||
<span class="badge badge-lg bg-ankara-red-100 dark:bg-ankara-red-600/20 text-ankara-red-700 dark:text-ankara-red-300">Community Focus</span>
|
||||
<span class="badge badge-lg bg-adire-blue-100 dark:bg-adire-blue-600/20 text-adire-blue-700 dark:text-adire-blue-300">Inclusive</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,13 +69,13 @@ import HomeGallery from '../components/HomeGallery.astro';
|
||||
</section>
|
||||
|
||||
<!-- Events Section -->
|
||||
<section class="section bg-gray-50" data-animate-on-scroll="fade-in">
|
||||
<section class="section bg-gray-50 dark:bg-gray-900" data-animate-on-scroll="fade-in">
|
||||
<div class="container">
|
||||
<div class="text-center mb-12">
|
||||
<h2 class="font-headline text-4xl font-bold text-nigerian-green-700 mb-4">
|
||||
Upcoming <span class="text-kente-gold-700">Events</span>
|
||||
<h2 class="font-headline text-4xl font-bold text-nigerian-green-700 dark:text-kente-gold-400 mb-4">
|
||||
Upcoming <span class="text-kente-gold-700 dark:text-kente-gold-300">Events</span>
|
||||
</h2>
|
||||
<p class="text-lg text-gray-600 max-w-2xl mx-auto">
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
Join us in celebrating our rich cultural heritage and building stronger community bonds.
|
||||
</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>
|
||||
@@ -102,7 +102,7 @@ import HomeGallery from '../components/HomeGallery.astro';
|
||||
</div>
|
||||
<a href="/events" class="inline-flex items-center text-kente-gold-600 hover:text-kente-gold-800 font-semibold transition-colors duration-200">
|
||||
Details
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="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>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -128,7 +128,7 @@ import HomeGallery from '../components/HomeGallery.astro';
|
||||
</div>
|
||||
<a href="/events" class="inline-flex items-center text-kente-gold-600 hover:text-kente-gold-800 font-semibold transition-colors duration-200">
|
||||
Details
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="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>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -154,7 +154,7 @@ import HomeGallery from '../components/HomeGallery.astro';
|
||||
</div>
|
||||
<a href="/events" class="inline-flex items-center text-kente-gold-600 hover:text-kente-gold-800 font-semibold transition-colors duration-200">
|
||||
Details
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="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>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -173,7 +173,7 @@ import HomeGallery from '../components/HomeGallery.astro';
|
||||
</section>
|
||||
|
||||
<!-- Donation Section -->
|
||||
<section class="section bg-gradient-to-br from-ankara-red-500 via-kente-gold-500 to-nigerian-green-500 text-white text-center relative overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<section class="section brand-surface text-white text-center relative overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<div class="absolute top-10 left-10 w-32 h-32 bg-white/10 rounded-full animate-float"></div>
|
||||
<div class="absolute bottom-10 right-10 w-24 h-24 bg-white/10 rounded-full animate-float animation-delay-1s"></div>
|
||||
|
||||
@@ -187,8 +187,7 @@ import HomeGallery from '../components/HomeGallery.astro';
|
||||
</div>
|
||||
|
||||
<h2 class="font-headline text-5xl font-bold mb-6 leading-tight">
|
||||
Make A Donation For Our Various
|
||||
<span class="bg-clip-text text-transparent bg-gradient-to-r from-white to-kente-gold-200">Projects</span>
|
||||
Support Our <span class="text-kente-gold-200">Community Programs</span>
|
||||
</h2>
|
||||
|
||||
<p class="text-lg opacity-90 leading-relaxed max-w-3xl mx-auto mb-8">
|
||||
@@ -207,7 +206,7 @@ import HomeGallery from '../components/HomeGallery.astro';
|
||||
</div>
|
||||
|
||||
<a href="/donate" class="btn btn-lg bg-white text-ankara-red-600 hover:bg-gray-100 shadow-xl hover:shadow-2xl">
|
||||
❤️ DONATE NOW ✈️
|
||||
❤️ Donate now
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
@@ -6,36 +6,31 @@ const members = memberFiles[0]?.frontmatter?.members || [];
|
||||
---
|
||||
|
||||
<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>
|
||||
<!-- Page Header (modern) -->
|
||||
<section class="brand-surface relative py-20 px-4 text-center text-white overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<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>
|
||||
<div class="flex items-center justify-center gap-2 mb-4 text-sm text-white/90">
|
||||
<a href="/" class="hover:underline">Home</a>
|
||||
<span class="opacity-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>
|
||||
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight">Our Members</h1>
|
||||
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">Dedicated people at the heart of Omoluabi.</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>
|
||||
<div class="text-gray-700 whitespace-pre-line">{intro}</div>
|
||||
<section class="max-w-3xl mx-auto mt-8 mb-12 bg-white dark:bg-gray-800 rounded-xl shadow p-6" data-animate-on-scroll="fade-in">
|
||||
<h2 class="text-xl font-bold mb-4 text-nigerian-green-700 dark:text-kente-gold-400">Membership Benefits/Welfare Packages</h2>
|
||||
<div class="text-gray-700 dark:text-gray-300 whitespace-pre-line">{intro}</div>
|
||||
</section>
|
||||
|
||||
<section class="max-w-6xl mx-auto mb-20" data-animate-on-scroll="fade-in">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8 px-4">
|
||||
{Array.isArray(members) && members.map((member: any) => (
|
||||
<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">
|
||||
<div class="card bg-white dark:bg-gray-800 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" />
|
||||
<h3 class="font-bold text-lg text-primary mb-1">{member.name}</h3>
|
||||
<p class="text-sm text-gray-600">{member.role}</p>
|
||||
<h3 class="font-bold text-lg text-gray-900 dark:text-white mb-1">{member.name}</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300">{member.role}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
@@ -3,33 +3,28 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
---
|
||||
|
||||
<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>
|
||||
<!-- Page Header (modern) -->
|
||||
<section class="brand-surface relative py-20 px-4 text-center text-white overflow-hidden" data-animate-on-scroll="fade-in">
|
||||
<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>
|
||||
<div class="flex items-center justify-center gap-2 mb-4 text-sm text-white/90">
|
||||
<a href="/" class="hover:underline">Home</a>
|
||||
<span class="opacity-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>
|
||||
<h1 class="font-headline text-5xl md:text-6xl font-extrabold mb-4 leading-tight">Our Orphanage Program</h1>
|
||||
<p class="text-lg md:text-xl opacity-90 max-w-2xl mx-auto">How we support children with care, education, and hope.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" data-animate-on-scroll="fade-in">
|
||||
<section class="section bg-white dark:bg-gray-900" 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">
|
||||
<h2 class="font-headline text-3xl font-bold text-nigerian-green-700 dark:text-kente-gold-400 mb-4">About the Orphanage</h2>
|
||||
<p class="text-gray-700 dark:text-gray-300 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">
|
||||
<p class="text-gray-700 dark:text-gray-300 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">
|
||||
@@ -47,43 +42,43 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section bg-gray-50" data-animate-on-scroll="fade-in">
|
||||
<section class="section bg-gray-50 dark:bg-gray-900" 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">
|
||||
<h2 class="font-headline text-4xl font-bold text-gray-900 dark:text-white 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">
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300 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">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border border-gray-200 dark:border-gray-700" 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">
|
||||
<p class="text-gray-700 dark:text-gray-300 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;">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border border-gray-200 dark:border-gray-700" 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">
|
||||
<p class="text-gray-700 dark:text-gray-300 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;">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border border-gray-200 dark:border-gray-700" 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">
|
||||
<p class="text-gray-700 dark:text-gray-300 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;">
|
||||
<div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-md border border-gray-200 dark:border-gray-700" 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">
|
||||
<p class="text-gray-700 dark:text-gray-300 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>
|
||||
|
@@ -11,6 +11,15 @@
|
||||
--kente-gold: #f59e0b;
|
||||
--ankara-red: #dc2626;
|
||||
--adire-blue: #2563eb;
|
||||
/* Dutch Flag Colors */
|
||||
--dutch-red: #21468b;
|
||||
--dutch-white: #ffffff;
|
||||
--dutch-blue: #1e4785;
|
||||
}
|
||||
|
||||
/* Dark theme tokens */
|
||||
.dark {
|
||||
--dutch-white: #0b0f14;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
|
@@ -211,11 +211,29 @@
|
||||
box-shadow: 0 0 20px rgba(22, 163, 74, 0.6);
|
||||
}
|
||||
|
||||
/* Nigerian Flag Colors */
|
||||
.nigerian-gradient {
|
||||
background: linear-gradient(90deg, #16a34a 0%, #ffffff 50%, #16a34a 100%);
|
||||
/* Nigerian Flag Background (vertical stripes) */
|
||||
.nigerian-flag-bg {
|
||||
background: linear-gradient(90deg, var(--nigerian-green) 0%, var(--nigerian-green) 33.33%, #fff 33.33%, #fff 66.66%, var(--nigerian-green) 66.66%, var(--nigerian-green) 100%);
|
||||
}
|
||||
|
||||
/* Dutch Flag Badge (horizontal stripes) */
|
||||
.dutch-flag-badge {
|
||||
width: 48px;
|
||||
height: 16px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.08);
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
.dutch-flag-badge .stripe {
|
||||
height: 33.33%;
|
||||
width: 100%;
|
||||
}
|
||||
.dutch-flag-badge .red { background: var(--dutch-red); }
|
||||
.dutch-flag-badge .white { background: #fff; }
|
||||
.dutch-flag-badge .blue { background: var(--dutch-blue); }
|
||||
|
||||
/* Kente Pattern Inspired Background */
|
||||
.kente-pattern {
|
||||
background-image:
|
||||
@@ -342,3 +360,54 @@
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
/* Nigerian + Dutch Flag Combined Gradient */
|
||||
.combined-flag-gradient {
|
||||
background: linear-gradient(90deg, var(--nigerian-green) 0%, var(--dutch-white) 20%, var(--dutch-red) 40%, var(--dutch-white) 60%, var(--dutch-blue) 80%, var(--nigerian-green) 100%);
|
||||
}
|
||||
|
||||
/* Subtle brand surface (used for CTA sections) */
|
||||
.brand-surface {
|
||||
background: radial-gradient(1200px 600px at 20% 20%, rgba(10,10,10,0.08), rgba(10,10,10,0) 60%),
|
||||
linear-gradient(135deg, rgba(12, 84, 36, 0.95) 0%, rgba(29, 78, 216, 0.90) 100%);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark .brand-surface {
|
||||
background: radial-gradient(1200px 600px at 20% 20%, rgba(255,255,255,0.04), rgba(255,255,255,0) 60%),
|
||||
linear-gradient(135deg, rgba(6, 59, 21, 0.95) 0%, rgba(23, 37, 84, 0.95) 100%);
|
||||
}
|
||||
|
||||
/* Combined Flag Button */
|
||||
.btn-combined-flag {
|
||||
background: linear-gradient(135deg, var(--nigerian-green) 0%, var(--dutch-red) 33%, var(--dutch-white) 66%, var(--dutch-blue) 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 0.75rem;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-combined-flag::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, var(--dutch-blue) 0%, var(--dutch-white) 50%, var(--nigerian-green) 100%);
|
||||
transition: left 0.3s ease;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.btn-combined-flag:hover::before {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.btn-combined-flag:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
// tailwind.config.mjs
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: 'class',
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
|
||||
theme: {
|
||||
extend: {
|
||||
|
Reference in New Issue
Block a user