Content accuracy pass, nav redesign, and frontend polish
- Verified all pages against lad1908.org via puppeteer scrape
- About: official DDBHH mission/vision/values, current 2023-2027 board
- Membership: real bylaws types (Full/Associate/Honorary/Student/Senior),
real payment methods (Venmo @LAD1908, mail check)
- Donate: real $100K stat, real established funds, real mailing address
- Contact: added mailing address card
- Homepage: removed stale May 23 banner, updated to official DDBHH
language and correct mission statement
- Updates: removed inaccurate members-only footer note (all 235 public)
- Deaf Focus: local logo (no longer depends on Directus), 30-day advance
notice CTA, portal.deaffocus.org/request button, fax + business hours
- Add sanitize-html.ts utility — strips Facebook CSS classes and editor
data-attributes from HTML content before dangerouslySetInnerHTML
- Render update.body, event.description, article.body as sanitized HTML
- Fix Deaf capitalization throughout all pages and metadata
- Redesign nav: 9 flat links → About/Updates/Events/Deaf Focus/
Get Involved dropdown (Membership, Minutes, Contact) + Donate button
- Add nav-dropdown CSS classes with high-contrast theme variants
- Fix homepage Hero gap: py-16 matches other pages; initial={false} on
Framer Motion prevents SSR opacity:0 invisible flash
This commit is contained in:
parent
db8bc58691
commit
224a219dbb
16 changed files with 431 additions and 500 deletions
2
frontend/next-env.d.ts
vendored
2
frontend/next-env.d.ts
vendored
|
|
@ -1,6 +1,6 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
|
|
|||
BIN
frontend/public/images/deaf-focus-logo.png
Normal file
BIN
frontend/public/images/deaf-focus-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 636 KiB |
|
|
@ -4,7 +4,7 @@ import type { Metadata } from 'next';
|
|||
|
||||
export const metadata: Metadata = {
|
||||
title: 'About Us | Louisiana Association of the Deaf',
|
||||
description: 'Learn about the history, mission, and vision of the Louisiana Association of the Deaf, serving the deaf community since 1908.',
|
||||
description: 'Learn about the Louisiana Association of the Deaf — the oldest, largest, and premier nonprofit organization for the Deaf, DeafBlind, and hard of hearing community in Louisiana since 1908.',
|
||||
};
|
||||
|
||||
export default function AboutPage() {
|
||||
|
|
@ -16,84 +16,113 @@ export default function AboutPage() {
|
|||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h1 className="hero-title text-4xl md:text-5xl font-bold mb-6">About Louisiana Association of the Deaf</h1>
|
||||
<p className="hero-subtitle text-xl">
|
||||
Connecting and empowering the deaf community in Louisiana since 1908.
|
||||
The oldest, largest, and premier nonprofit organization for the Deaf, DeafBlind, and hard of hearing community in Louisiana since 1908.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Mission and Vision */}
|
||||
{/* About Us */}
|
||||
<section className="py-16 bg-color-bg">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
|
||||
<div className="card event-card-gold-border p-8 space-y-6">
|
||||
<h2 className="section-heading event-card-title-gold text-3xl font-bold">Our Mission</h2>
|
||||
<p className="section-text text-lg">
|
||||
To promote social, educational, and recreational opportunities for deaf individuals and their families while fostering a sense of community and belonging.
|
||||
</p>
|
||||
<p className="section-text text-lg">
|
||||
We strive to create an inclusive environment where deaf and hard-of-hearing individuals can connect, share experiences, and access resources.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-8 space-y-6">
|
||||
<h2 className="section-heading event-card-title-gold text-3xl font-bold">Our Vision</h2>
|
||||
<p className="section-text text-lg">
|
||||
To be a leading organization that bridges the gap between the deaf and hearing communities, promoting awareness, understanding, and equal opportunities.
|
||||
</p>
|
||||
<p className="section-text text-lg">
|
||||
We envision a world where deaf and hard-of-hearing individuals have full access to communication, education, and community participation.
|
||||
</p>
|
||||
<h2 className="section-heading text-3xl font-bold mb-8 text-center">About Us</h2>
|
||||
<div className="card video-container-gold p-8 space-y-6">
|
||||
<p className="section-text text-lg">
|
||||
Louisiana Association of the Deaf (LAD) is the oldest, largest, and premier nonprofit organization for the Deaf, DeafBlind, and hard of hearing (DDBHH) citizens in the state of Louisiana. LAD was founded in 1908 by the alumni of the Louisiana School for the Deaf and is one of fifty (50) states affiliated with the National Association of the Deaf (NAD). LAD advocates the promotion of the educational, economic, and social well-being of DDBHH individuals in Louisiana.
|
||||
</p>
|
||||
<div>
|
||||
<p className="section-text text-lg mb-3">In addition, LAD, doing business as Deaf Focus, provides a variety of services to the DDBHH community, including:</p>
|
||||
<ul className="section-text text-lg space-y-1 list-disc list-inside ml-4">
|
||||
<li>Interpreting Services</li>
|
||||
<li>Educational Support/Tutoring</li>
|
||||
<li>Expert Consulting</li>
|
||||
<li>Law Enforcement & First Responders Training</li>
|
||||
<li>Professional Development</li>
|
||||
<li>Health and Wellness Services</li>
|
||||
<li>Social and Recreational Activities</li>
|
||||
<li>ASL Classes</li>
|
||||
<li>Advocacy: Legislative, Education, Services, Employment, and Access</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p className="section-text text-lg">
|
||||
LAD is supported by its members and other stakeholders interested in achieving LAD's goals. Membership is open to any Louisiana citizen who is DDBHH; parents of children who are DDBHH; professionals who work with DDBHH youth and adults; including those who are not deaf, and other DDBHH individuals outside of Louisiana, interested in supporting our mission.
|
||||
</p>
|
||||
<p className="section-text text-lg">
|
||||
Our organization is Deaf-led with a team passionate about the Deaf-Centric approach in the provision of our services. We believe collaboration is essential to achieving our goals, and we strive to collaborate with various community partners, businesses, schools, government agencies, and other non-profit organizations. We aim to be a model example of a Deaf-Led organization. Together, we can elevate the journey of positive and meaningful progress in creating a more inclusive world where everyone has the opportunity to succeed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* History */}
|
||||
{/* Mission, Vision, Values */}
|
||||
<section className="py-16 bg-color-bg-secondary">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="section-heading text-3xl font-bold mb-8 text-center">Our History</h2>
|
||||
<div className="card video-container-gold p-8 space-y-6">
|
||||
<p className="section-text text-lg">
|
||||
The Louisiana Association of the Deaf (LAD) was established in 1908 by a group of deaf community members who recognized the need for a dedicated organization to serve the growing deaf population in Louisiana.
|
||||
</p>
|
||||
<p className="section-text text-lg">
|
||||
Starting with just a handful of members, LAD quickly grew into a vibrant community hub, hosting regular social events, educational workshops, and advocacy initiatives.
|
||||
</p>
|
||||
<p className="section-text text-lg">
|
||||
Throughout the decades, LAD has continued to evolve and expand its services while staying true to its core mission of fostering community and providing support for deaf and hard-of-hearing individuals in Louisiana and surrounding areas.
|
||||
</p>
|
||||
<h2 className="section-heading text-3xl font-bold mb-8 text-center">Mission, Vision & Values</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
|
||||
<div className="card event-card-gold-border p-8 space-y-4">
|
||||
<h3 className="section-heading event-card-title-gold text-2xl font-bold">Mission</h3>
|
||||
<p className="section-text text-lg">
|
||||
The mission of the LAD is to preserve, protect, and promote the civil, human, and linguistic rights of deaf citizens of Louisiana.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-8 space-y-4">
|
||||
<h3 className="section-heading event-card-title-gold text-2xl font-bold">Vision</h3>
|
||||
<p className="section-text text-lg">
|
||||
The vision of the LAD is a society in which the language, culture, and heritage of deaf Louisianans are acknowledged and respected. As a result, deaf people will have the fullness of life, liberty, and equality.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{[
|
||||
{ title: 'Service', desc: 'Every deaf person should have equal access to any service provided to tax-paying citizens. We advocate to raise the bar in services, public or private, and awareness within society.' },
|
||||
{ title: 'Education', desc: 'We believe in Free and Appropriate Public Education with emphasis on early access for establishing a language foundation, and promotion of lifelong learning for all adults.' },
|
||||
{ title: 'Social', desc: 'We believe in preserving our heritage, culture, and the status of our linguistic minority. We promote every opportunity where deaf people can engage and build relationships.' },
|
||||
{ title: 'Welfare', desc: 'We believe in nourishing every deaf person\'s well-being toward healthy self-esteem, self-confidence, and self-advocacy abilities so that every deaf person can lead a self-sufficient life.' },
|
||||
].map((v) => (
|
||||
<div key={v.title} className="card event-card-gold-border p-6 text-center">
|
||||
<h4 className="benefit-card-title event-card-title-gold text-lg font-semibold mb-3">{v.title}</h4>
|
||||
<p className="benefit-card-description text-sm">{v.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Board Members */}
|
||||
{/* Board of Directors */}
|
||||
<section className="py-16 bg-color-bg">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="section-heading text-3xl font-bold mb-8 text-center">Leadership Team</h2>
|
||||
<p className="section-text text-lg text-center mb-12">
|
||||
LAD is governed by an elected board representing the deaf community across Louisiana.
|
||||
The 2019–2021 board:
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<h2 className="section-heading text-3xl font-bold mb-4 text-center">Board of Directors</h2>
|
||||
<p className="section-text text-lg text-center mb-2">
|
||||
LAD Board of Directors for the 2023–2027 term.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||
<p className="section-text text-sm text-center mb-10">
|
||||
The Board of Directors is elected by the general membership during the organization's biennial conference and governs the LAD. Board members are DDBHH and serve on a voluntary basis.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{[
|
||||
{ name: 'Jimmy Challis Gore', role: 'President' },
|
||||
{ name: 'Sherry Crosby', role: 'Vice President' },
|
||||
{ name: 'Jay Isch', role: 'Secretary' },
|
||||
{ name: 'Alla Tarasyuk', role: 'Treasurer' },
|
||||
{ name: 'Sherry Crosby', role: 'President', email: 'President@lad1908.org' },
|
||||
{ name: 'Jerrod Wingate', role: 'Vice President', email: 'VicePresident@lad1908.org' },
|
||||
{ name: 'Cammy Gaspar', role: 'Secretary', email: 'Secretary@lad1908.org' },
|
||||
{ name: 'Ben Wimberly', role: 'Treasurer', email: 'Treasurer@lad1908.org' },
|
||||
{ name: 'Katrina Williams', role: 'Member-at-Large', email: 'MAL1@lad1908.org' },
|
||||
{ name: 'Vacant', role: 'Member-at-Large', email: 'MAL2@lad1908.org' },
|
||||
{ name: 'Rosalyn Watson', role: 'Member-at-Large', email: 'MAL3@lad1908.org' },
|
||||
{ name: 'Jay Isch', role: 'Executive Director (ex-officio)', email: 'Jay@deaffocus.org' },
|
||||
].map((member) => (
|
||||
<div key={member.name} className="card event-card-gold-border p-6 text-center">
|
||||
<div className="card-image-placeholder w-20 h-20 rounded-full mx-auto mb-4 flex items-center justify-center">
|
||||
<span className="card-placeholder-text text-2xl font-bold">
|
||||
{member.name.charAt(0)}
|
||||
<div key={member.role + member.name} className="card event-card-gold-border p-6 text-center">
|
||||
<div className="card-image-placeholder w-16 h-16 rounded-full mx-auto mb-3 flex items-center justify-center">
|
||||
<span className="card-placeholder-text text-xl font-bold">
|
||||
{member.name === 'Vacant' ? '?' : member.name.charAt(0)}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="card-title event-card-title-gold text-lg font-semibold">{member.name}</h3>
|
||||
<p className="card-date text-sm">{member.role}</p>
|
||||
<h3 className="card-title event-card-title-gold text-base font-semibold leading-tight">{member.name}</h3>
|
||||
<p className="card-date text-xs mt-1">{member.role}</p>
|
||||
<a href={`mailto:${member.email}`} className="card-link text-xs mt-1 block truncate">{member.email}</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -109,15 +138,13 @@ export default function AboutPage() {
|
|||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{[
|
||||
{ abbr: 'NAD', name: 'National Association of the Deaf', desc: 'LAD\'s national affiliate — the nation\'s premier civil rights organization for the Deaf.' },
|
||||
{ abbr: 'LCD', name: 'Louisiana Commission for the Deaf', desc: 'State agency serving as the official liaison between the deaf community and state government.' },
|
||||
{ abbr: 'LSD', name: 'Louisiana School for the Deaf', desc: 'Residential school providing K–12 education in ASL for deaf students statewide.' },
|
||||
{ abbr: 'LCD', name: 'Louisiana Commission for the Deaf', desc: 'State agency serving as the official liaison between the Deaf community and state government.' },
|
||||
{ abbr: 'LSD', name: 'Louisiana School for the Deaf', desc: 'Residential school providing K–12 education in ASL for Deaf students statewide.' },
|
||||
{ abbr: 'Deaf Focus', name: 'Deaf Focus', desc: 'Deaf-led services organization operated by LAD — interpreting, advocacy, and ASL classes.' },
|
||||
{ abbr: 'BRAD', name: 'Baton Rouge Association of the Deaf', desc: 'Local affiliate serving the deaf community in the Greater Baton Rouge area.' },
|
||||
{ abbr: 'GNOAD', name: 'Greater New Orleans Association of the Deaf', desc: 'Local affiliate serving the deaf community in the New Orleans area.' },
|
||||
{ abbr: 'BRAD', name: 'Baton Rouge Association of the Deaf', desc: 'Local affiliate serving the Deaf community in the Greater Baton Rouge area.' },
|
||||
{ abbr: 'GNOAD', name: 'Greater New Orleans Association of the Deaf', desc: 'Local affiliate serving the Deaf community in the New Orleans area.' },
|
||||
{ abbr: 'NOBDA', name: 'New Orleans Black Deaf Association', desc: 'Organization serving the Black Deaf community in New Orleans.' },
|
||||
{ abbr: 'LRID', name: 'Louisiana Registry of Interpreters for the Deaf', desc: 'Professional organization for sign language interpreters in Louisiana.' },
|
||||
{ abbr: 'DGM-LA', name: 'Deaf Grassroots Movement of Louisiana', desc: 'Endorsed by LAD — grassroots advocacy organization for the Deaf community statewide.' },
|
||||
{ abbr: 'LAAD', name: 'Lafayette Athletic Association of the Deaf', desc: 'Local affiliate serving the deaf community in the Lafayette area.' },
|
||||
].map((org) => (
|
||||
<div key={org.abbr} className="card event-card-gold-border p-5 flex gap-4 items-start">
|
||||
<span className="text-[var(--color-primary)] font-bold text-lg w-20 shrink-0">{org.abbr}</span>
|
||||
|
|
@ -132,41 +159,12 @@ export default function AboutPage() {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{/* Values */}
|
||||
<section className="py-16 bg-color-bg">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="section-heading text-3xl font-bold mb-8 text-center">Our Values</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div className="card event-card-gold-border p-6 text-center">
|
||||
<h3 className="benefit-card-title event-card-title-gold text-xl font-semibold mb-4">Inclusion</h3>
|
||||
<p className="benefit-card-description">
|
||||
We welcome and embrace individuals of all backgrounds, abilities, and experiences.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-6 text-center">
|
||||
<h3 className="benefit-card-title event-card-title-gold text-xl font-semibold mb-4">Accessibility</h3>
|
||||
<p className="benefit-card-description">
|
||||
We are committed to removing barriers and ensuring full access to communication and resources.
|
||||
</p>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-6 text-center">
|
||||
<h3 className="benefit-card-title event-card-title-gold text-xl font-semibold mb-4">Community</h3>
|
||||
<p className="benefit-card-description">
|
||||
We foster meaningful connections and a sense of belonging for all members.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="py-16 cta-section">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="cta-title text-3xl font-bold mb-4">Get Involved</h2>
|
||||
<p className="cta-description text-xl max-w-3xl mx-auto mb-8">
|
||||
Join us in our mission to support and empower the deaf community in Louisiana.
|
||||
Join us in our mission to support and empower the Deaf community in Louisiana.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<Link
|
||||
|
|
|
|||
|
|
@ -61,6 +61,15 @@ export default function ContactPage() {
|
|||
<p className="section-text">Hours: Mon–Fri, 9am–4pm</p>
|
||||
</div>
|
||||
|
||||
<div className="card event-card-gold-border p-4">
|
||||
<h3 className="card-title event-card-title-gold text-xl font-semibold mb-2">Mailing Address</h3>
|
||||
<p className="section-text">
|
||||
Louisiana Association of the Deaf<br />
|
||||
3112 Valley Creek Drive, Suite E<br />
|
||||
Baton Rouge, LA 70808
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="card event-card-gold-border p-4">
|
||||
<h3 className="card-title event-card-title-gold text-xl font-semibold mb-2">Follow Us</h3>
|
||||
<div className="flex space-x-4">
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import type { Metadata } from 'next';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { sanitizeHtml } from '@/utils/sanitize-html';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Deaf Focus | Louisiana Association of the Deaf',
|
||||
|
|
@ -41,7 +42,7 @@ export default async function DeafFocusPage() {
|
|||
<div className="max-w-3xl mx-auto text-center">
|
||||
<div className="flex justify-center mb-6">
|
||||
<Image
|
||||
src="https://db.lad1908.org/assets/c15110fe-d25a-4863-8e9c-055bfad3c710"
|
||||
src="/images/deaf-focus-logo.png"
|
||||
alt="Deaf Focus logo"
|
||||
width={120}
|
||||
height={120}
|
||||
|
|
@ -78,11 +79,10 @@ export default async function DeafFocusPage() {
|
|||
{article.title}
|
||||
</h2>
|
||||
{article.body && (
|
||||
<div className="section-text space-y-3">
|
||||
{article.body.split('\n').map((line, i) => (
|
||||
<p key={i}>{line}</p>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className="section-text prose prose-sm max-w-none"
|
||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(article.body) }}
|
||||
/>
|
||||
)}
|
||||
</article>
|
||||
))
|
||||
|
|
@ -97,12 +97,20 @@ export default async function DeafFocusPage() {
|
|||
<h2 className="cta-title text-3xl font-bold mb-4">Request an Interpreter</h2>
|
||||
<p className="cta-description text-xl max-w-3xl mx-auto mb-8">
|
||||
Deaf Focus provides professional, certified sign language interpreters throughout Louisiana.
|
||||
Contact us at least 10 business days in advance.
|
||||
Submit requests at least 30 days in advance for best availability.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<a
|
||||
href="mailto:request@deaffocus.org"
|
||||
href="https://portal.deaffocus.org/request"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="cta-button-primary px-6 py-3 rounded-lg font-medium text-lg inline-block"
|
||||
>
|
||||
Submit Online Request
|
||||
</a>
|
||||
<a
|
||||
href="mailto:request@deaffocus.org"
|
||||
className="cta-button-secondary px-6 py-3 rounded-lg font-medium text-lg inline-block"
|
||||
>
|
||||
Email Request
|
||||
</a>
|
||||
|
|
@ -113,6 +121,7 @@ export default async function DeafFocusPage() {
|
|||
Call (225) 319-5586
|
||||
</a>
|
||||
</div>
|
||||
<p className="cta-description text-sm mt-4">Fax: (225) 308-4025 · Hours: Mon–Fri, 9am–4pm</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -96,7 +96,10 @@ export default function DonatePage() {
|
|||
{/* Impact */}
|
||||
<section className="py-16 bg-color-bg">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="section-heading text-3xl font-bold mb-12 text-center">Your Impact</h2>
|
||||
<h2 className="section-heading text-3xl font-bold mb-4 text-center">Your Impact</h2>
|
||||
<p className="section-text text-xl text-center mb-12 max-w-3xl mx-auto">
|
||||
Since 2019, LAD has disbursed over <strong>$100,000</strong> directly supporting our community — services, project funding, sponsorships, scholarships, and more.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-12 max-w-5xl mx-auto">
|
||||
<div>
|
||||
<h3 className="card-title text-2xl font-bold mb-6">How Your Donation Helps</h3>
|
||||
|
|
@ -104,71 +107,41 @@ export default function DonatePage() {
|
|||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-3">•</span>
|
||||
<p className="section-text">
|
||||
<strong>Community Events:</strong> Funds community gatherings, social events, and cultural celebrations that bring our deaf community together.
|
||||
<strong>Community Events:</strong> Funds community gatherings, social events, and cultural celebrations that bring our Deaf community together.
|
||||
</p>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-3">•</span>
|
||||
<p className="section-text">
|
||||
<strong>Educational Workshops:</strong> Supports ASL classes, interpreter training, and educational workshops for members and the broader community.
|
||||
<strong>Educational Programs:</strong> Supports ASL classes, tutoring, and educational workshops for members and the broader community.
|
||||
</p>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-3">•</span>
|
||||
<p className="section-text">
|
||||
<strong>Advocacy:</strong> Enables our work advocating for accessibility and deaf rights in local businesses, schools, and government.
|
||||
<strong>Advocacy:</strong> Enables our work advancing Deaf rights through legislation, policy, and public awareness.
|
||||
</p>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-3">•</span>
|
||||
<p className="section-text">
|
||||
<strong>Youth Programs:</strong> Funds activities and mentorship opportunities for deaf and hard-of-hearing youth in our community.
|
||||
<strong>Youth & Emergency:</strong> Supports Deaf youth programs, toy drives, book drives, and emergency disaster relief.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="card-title text-2xl font-bold mb-6">Donation Allocation</h3>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="section-text font-medium">Community Events</span>
|
||||
<span className="section-text font-medium">40%</span>
|
||||
<h3 className="card-title text-2xl font-bold mb-6">Established Funds</h3>
|
||||
<p className="section-text mb-4">You may designate your contribution to a specific fund:</p>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{['Youth Fund', 'Apparel Fund', 'Book Drive Fund', 'Toy Drive Fund', 'Office Renovations Fund', 'Emergency Disaster Fund'].map((fund) => (
|
||||
<div key={fund} className="card event-card-gold-border p-3 text-center">
|
||||
<p className="card-title text-sm font-semibold">{fund}</p>
|
||||
</div>
|
||||
<div className="w-full video-container rounded-full h-2.5">
|
||||
<div className="bg-primary h-2.5 rounded-full" style={{ width: '40%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="section-text font-medium">Educational Programs</span>
|
||||
<span className="section-text font-medium">30%</span>
|
||||
</div>
|
||||
<div className="w-full video-container rounded-full h-2.5">
|
||||
<div className="bg-primary h-2.5 rounded-full" style={{ width: '30%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="section-text font-medium">Advocacy</span>
|
||||
<span className="section-text font-medium">15%</span>
|
||||
</div>
|
||||
<div className="w-full video-container rounded-full h-2.5">
|
||||
<div className="bg-primary h-2.5 rounded-full" style={{ width: '15%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="section-text font-medium">Youth Programs</span>
|
||||
<span className="section-text font-medium">15%</span>
|
||||
</div>
|
||||
<div className="w-full video-container rounded-full h-2.5">
|
||||
<div className="bg-primary h-2.5 rounded-full" style={{ width: '15%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="mt-4 card-date text-sm">
|
||||
Financial information and annual reports are available upon request.
|
||||
Undesignated contributions support general operations. Financial information and annual reports are available upon request.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -181,7 +154,7 @@ export default function DonatePage() {
|
|||
<h2 className="section-heading text-3xl font-bold mb-8 text-center">Corporate Sponsorship</h2>
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<p className="section-text text-lg mb-8">
|
||||
We welcome partnerships with businesses and organizations that share our commitment to the deaf community.
|
||||
We welcome partnerships with businesses and organizations that share our commitment to the Deaf community.
|
||||
Corporate sponsors receive recognition in our events, publications, and website.
|
||||
</p>
|
||||
<Link href="/contact" className="btn-primary inline-block px-8 py-3 rounded-lg">
|
||||
|
|
@ -211,7 +184,11 @@ export default function DonatePage() {
|
|||
<div className="card event-card-gold-border p-6">
|
||||
<h3 className="card-title event-card-title-gold text-xl font-semibold mb-2">Do you accept donations by check or mail?</h3>
|
||||
<p className="section-text">
|
||||
Yes, checks can be made payable to "Louisiana Association of the Deaf" and mailed to our office address. Please contact us for the current mailing address.
|
||||
Yes. Make checks or money orders payable to <strong>Louisiana Association of the Deaf</strong> and mail to:
|
||||
</p>
|
||||
<p className="section-text mt-2">
|
||||
3112 Valley Creek Drive, Suite E<br />
|
||||
Baton Rouge, LA 70808
|
||||
</p>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-6">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import type { Metadata } from 'next';
|
||||
import { sanitizeHtml } from '@/utils/sanitize-html';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Events | Louisiana Association of the Deaf',
|
||||
|
|
@ -47,7 +48,10 @@ const EventCard = ({ event }: { event: Event }) => (
|
|||
<h3 className="card-title event-card-title-gold text-xl font-semibold mb-2">{event.title}</h3>
|
||||
<p className="card-date font-medium text-sm mb-1">{formatDate(event.date)}{event.time ? ` · ${event.time}` : ''}</p>
|
||||
<p className="section-text text-sm mb-3">{event.location}</p>
|
||||
<p className="card-description mb-4 flex-grow">{event.description}</p>
|
||||
<div
|
||||
className="card-description mb-4 flex-grow prose prose-sm max-w-none"
|
||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(event.description ?? '') }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -519,6 +519,21 @@ main {
|
|||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.nav-dropdown {
|
||||
background-color: var(--color-bg);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.nav-dropdown-item {
|
||||
color: var(--color-text);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.nav-dropdown-item:hover {
|
||||
background-color: var(--color-bg-secondary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* ========================================== */
|
||||
/* HIGH CONTRAST NAVIGATION FIXES */
|
||||
/* ========================================== */
|
||||
|
|
@ -605,6 +620,36 @@ main {
|
|||
border: 3px solid #FFFFFF !important;
|
||||
}
|
||||
|
||||
.high-contrast-light .nav-dropdown {
|
||||
background-color: #FFFFFF !important;
|
||||
border: 2px solid #000000 !important;
|
||||
}
|
||||
|
||||
.high-contrast-light .nav-dropdown-item {
|
||||
color: #000000 !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.high-contrast-light .nav-dropdown-item:hover {
|
||||
background-color: #000000 !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.high-contrast-dark .nav-dropdown {
|
||||
background-color: #000000 !important;
|
||||
border: 2px solid #FFFFFF !important;
|
||||
}
|
||||
|
||||
.high-contrast-dark .nav-dropdown-item {
|
||||
color: #FFFFFF !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.high-contrast-dark .nav-dropdown-item:hover {
|
||||
background-color: #FFFFFF !important;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* ========================================== */
|
||||
/* HERO COMPONENTS */
|
||||
/* ========================================== */
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ const inter = Inter({
|
|||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Louisiana Association of the Deaf',
|
||||
description: 'The statewide organization serving the deaf and hard-of-hearing community in Louisiana since 1908.',
|
||||
description: 'The statewide organization serving the Deaf and hard-of-hearing community in Louisiana since 1908.',
|
||||
metadataBase: new URL('https://lad1908.org'),
|
||||
keywords: ['deaf', 'louisiana', 'LAD', 'association', 'ASL', 'events', 'advocacy'],
|
||||
keywords: ['Deaf', 'louisiana', 'LAD', 'association', 'ASL', 'events', 'advocacy'],
|
||||
authors: [{ name: 'Louisiana Association of the Deaf' }],
|
||||
openGraph: {
|
||||
title: 'Louisiana Association of the Deaf',
|
||||
description: 'The statewide organization serving the deaf and hard-of-hearing community in Louisiana since 1908.',
|
||||
description: 'The statewide organization serving the Deaf and hard-of-hearing community in Louisiana since 1908.',
|
||||
url: 'https://lad1908.org',
|
||||
siteName: 'Louisiana Association of the Deaf',
|
||||
images: [
|
||||
|
|
@ -38,7 +38,7 @@ export const metadata: Metadata = {
|
|||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: 'Louisiana Association of the Deaf',
|
||||
description: 'The statewide organization serving the deaf and hard-of-hearing community in Louisiana since 1908.',
|
||||
description: 'The statewide organization serving the Deaf and hard-of-hearing community in Louisiana since 1908.',
|
||||
images: ['/images/og-image.jpg'],
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type { Metadata } from 'next';
|
|||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Membership | Louisiana Association of the Deaf',
|
||||
description: 'Join the Louisiana Association of the Deaf and become part of our statewide community. Learn about membership benefits and how to apply.',
|
||||
description: 'Join the Louisiana Association of the Deaf. Membership is open to Deaf, DeafBlind, and hard of hearing individuals, their families, and allies across Louisiana.',
|
||||
};
|
||||
|
||||
export default function MembershipPage() {
|
||||
|
|
@ -16,7 +16,7 @@ export default function MembershipPage() {
|
|||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h1 className="hero-title text-4xl md:text-5xl font-bold mb-6">Become a Member</h1>
|
||||
<p className="hero-subtitle text-xl">
|
||||
Join our statewide community and connect with Deaf individuals and allies across Louisiana.
|
||||
Join our statewide community and connect with Deaf, DeafBlind, and hard of hearing individuals and allies across Louisiana.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -36,7 +36,7 @@ export default function MembershipPage() {
|
|||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-2">•</span>
|
||||
<span>Opportunities to build relationships with other deaf community members</span>
|
||||
<span>Opportunities to build relationships with other Deaf community members</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-2">•</span>
|
||||
|
|
@ -45,7 +45,7 @@ export default function MembershipPage() {
|
|||
</ul>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-6">
|
||||
<h3 className="benefit-card-title event-card-title-gold text-xl font-semibold mb-4">Club Governance</h3>
|
||||
<h3 className="benefit-card-title event-card-title-gold text-xl font-semibold mb-4">Governance & Voice</h3>
|
||||
<ul className="space-y-3 benefit-card-description">
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-2">•</span>
|
||||
|
|
@ -53,24 +53,24 @@ export default function MembershipPage() {
|
|||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-2">•</span>
|
||||
<span>Eligibility to run for board positions</span>
|
||||
<span>Eligibility to run for board positions (Full Members)</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-2">•</span>
|
||||
<span>Opportunity to shape the future of LAD through input and feedback</span>
|
||||
<span>Shape the future of LAD through input and feedback</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-6">
|
||||
<h3 className="benefit-card-title event-card-title-gold text-xl font-semibold mb-4">Resources & Support</h3>
|
||||
<h3 className="benefit-card-title event-card-title-gold text-xl font-semibold mb-4">Resources & Support</h3>
|
||||
<ul className="space-y-3 benefit-card-description">
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-2">•</span>
|
||||
<span>Access to club resources and information</span>
|
||||
<span>Access to members-only meeting minutes and documents</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-2">•</span>
|
||||
<span>Support network of deaf and hard-of-hearing individuals</span>
|
||||
<span>Support network of Deaf and hard-of-hearing individuals</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="card-date font-bold mr-2">•</span>
|
||||
|
|
@ -85,104 +85,44 @@ export default function MembershipPage() {
|
|||
{/* Membership Types */}
|
||||
<section className="py-16 bg-color-bg-secondary">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="section-heading text-3xl font-bold mb-12 text-center">Membership Types</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-5xl mx-auto">
|
||||
<div className="card video-container-gold p-6">
|
||||
<div className="text-center mb-6">
|
||||
<h3 className="card-title text-2xl font-bold mb-2">Regular</h3>
|
||||
<p className="card-date text-lg font-medium">Annual membership</p>
|
||||
<h2 className="section-heading text-3xl font-bold mb-4 text-center">Membership Types</h2>
|
||||
<p className="section-text text-center mb-10 max-w-2xl mx-auto">
|
||||
LAD membership is open to Deaf, DeafBlind, and hard of hearing individuals, their families, professionals, and allies.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-5xl mx-auto">
|
||||
{[
|
||||
{
|
||||
title: 'Full Member',
|
||||
who: 'Deaf individuals 18+ residing in Louisiana with dues paid.',
|
||||
rights: 'Full voting rights and eligibility to hold office.',
|
||||
},
|
||||
{
|
||||
title: 'Associate Member',
|
||||
who: 'Hearing individuals (in or out of Louisiana), or non-Louisiana Deaf individuals 18+ with dues paid.',
|
||||
rights: 'All rights of Full Members except holding office.',
|
||||
},
|
||||
{
|
||||
title: 'Honorary Member',
|
||||
who: 'Conferred by a two-thirds vote of members at a biennial Conference Meeting.',
|
||||
rights: 'All rights of Full Members.',
|
||||
},
|
||||
{
|
||||
title: 'Student Member',
|
||||
who: 'Deaf high school seniors may request membership.',
|
||||
rights: '2-year complimentary membership; all rights except holding office.',
|
||||
},
|
||||
{
|
||||
title: 'Senior Member',
|
||||
who: 'Senior Deaf community members meeting eligibility criteria.',
|
||||
rights: 'Reduced dues; full participation in LAD activities.',
|
||||
},
|
||||
].map((t) => (
|
||||
<div key={t.title} className="card event-card-gold-border p-6">
|
||||
<h3 className="benefit-card-title event-card-title-gold text-lg font-semibold mb-2">{t.title}</h3>
|
||||
<p className="card-description text-sm mb-2"><strong>Who:</strong> {t.who}</p>
|
||||
<p className="card-description text-sm"><strong>Rights:</strong> {t.rights}</p>
|
||||
</div>
|
||||
<ul className="space-y-3 card-description mb-8">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Standard membership for individuals 18+</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Full voting rights</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Access to all events and activities</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Eligibility for board positions</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="text-center">
|
||||
<Link href="/contact" className="btn-primary inline-block px-8 py-3 rounded-lg">Apply Now</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card video-container-gold p-6 border-primary">
|
||||
<div className="text-center mb-6">
|
||||
<span className="inline-block bg-primary text-white px-3 py-1 rounded-full text-sm font-medium mb-2">Most Popular</span>
|
||||
<h3 className="card-title text-2xl font-bold mb-2">Family</h3>
|
||||
<p className="card-date text-lg font-medium">Annual membership</p>
|
||||
</div>
|
||||
<ul className="space-y-3 card-description mb-8">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Covers 2 adults and children under 18</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Two voting memberships</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Access to all events and activities</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Eligibility for board positions</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Family-friendly event notifications</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="text-center">
|
||||
<Link href="/contact" className="btn-primary inline-block px-8 py-3 rounded-lg">Apply Now</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card video-container-gold p-6">
|
||||
<div className="text-center mb-6">
|
||||
<h3 className="card-title text-2xl font-bold mb-2">Lifetime</h3>
|
||||
<p className="card-date text-lg font-medium">One-time payment</p>
|
||||
</div>
|
||||
<ul className="space-y-3 card-description mb-8">
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>One-time payment for lifetime membership</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Full voting rights</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Access to all events and activities</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Eligibility for board positions</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<span className="text-primary font-bold mr-2">✓</span>
|
||||
<span>Recognition as lifetime supporter</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="text-center">
|
||||
<Link href="/contact" className="btn-primary inline-block px-8 py-3 rounded-lg">Apply Now</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-10 text-center">
|
||||
<p className="section-text">
|
||||
Please <Link href="/contact" className="card-link hover:underline"> contact us </Link>
|
||||
for current membership rates and any available discounts.
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -198,10 +138,9 @@ export default function MembershipPage() {
|
|||
1
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="card-title text-xl font-semibold mb-2">Complete Application</h3>
|
||||
<h3 className="card-title text-xl font-semibold mb-2">Complete the Membership Form</h3>
|
||||
<p className="section-text">
|
||||
Contact us through the website or attend one of our events to get a membership application form.
|
||||
You can also download the form and submit it via email.
|
||||
Download and fill out the membership application form. Contact us or attend one of our events to get a copy.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -211,9 +150,23 @@ export default function MembershipPage() {
|
|||
</div>
|
||||
<div>
|
||||
<h3 className="card-title text-xl font-semibold mb-2">Pay Membership Dues</h3>
|
||||
<p className="section-text">
|
||||
Submit your membership dues via check, cash, or online payment. Dues can be paid annually or as a one-time payment for lifetime membership.
|
||||
<p className="section-text mb-3">
|
||||
Submit your dues using one of the following methods:
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
<div className="card event-card-gold-border p-4">
|
||||
<p className="card-title font-semibold text-sm mb-1">Venmo</p>
|
||||
<p className="section-text text-sm">@LAD1908 — please include “Membership” in the note</p>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-4">
|
||||
<p className="card-title font-semibold text-sm mb-1">Mail a Check or Money Order</p>
|
||||
<p className="section-text text-sm">
|
||||
Payable to <strong>Louisiana Association of the Deaf</strong><br />
|
||||
3112 Valley Creek Drive, Suite E<br />
|
||||
Baton Rouge, LA 70808
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-6">
|
||||
|
|
@ -221,20 +174,9 @@ export default function MembershipPage() {
|
|||
3
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="card-title text-xl font-semibold mb-2">Board Approval</h3>
|
||||
<h3 className="card-title text-xl font-semibold mb-2">Get Connected</h3>
|
||||
<p className="section-text">
|
||||
All applications are reviewed and approved by the LAD board. This process typically takes 1-2 weeks.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-6">
|
||||
<div className="bg-primary text-white flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center font-bold text-xl">
|
||||
4
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="card-title text-xl font-semibold mb-2">Receive Membership Card</h3>
|
||||
<p className="section-text">
|
||||
Once approved, you'll receive your official LAD membership confirmation and welcome packet with information about upcoming events and activities.
|
||||
Once your membership is processed, you'll receive confirmation and gain access to member resources, upcoming events, and the LAD community.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -244,43 +186,13 @@ export default function MembershipPage() {
|
|||
href="/contact"
|
||||
className="btn-primary inline-block px-8 py-3 rounded-lg"
|
||||
>
|
||||
Request Application Form
|
||||
Contact Us to Get Started
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Testimonials */}
|
||||
<section className="py-16 bg-color-bg-secondary">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="section-heading text-3xl font-bold mb-12 text-center">What Our Members Say</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div className="card event-card-gold-border p-6">
|
||||
<blockquote className="section-text mb-4">
|
||||
"Joining LAD was one of the best decisions I've made. It's helped me connect with the Deaf community across Louisiana and make lifelong friends."
|
||||
</blockquote>
|
||||
<div className="card-title font-semibold">Sarah K.</div>
|
||||
<div className="card-date text-sm">Member since 2019</div>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-6">
|
||||
<blockquote className="section-text mb-4">
|
||||
"As a parent of a deaf child, LAD has been an invaluable resource for our family. The events and workshops have helped us all learn and grow together."
|
||||
</blockquote>
|
||||
<div className="card-title font-semibold">David M.</div>
|
||||
<div className="card-date text-sm">Member since 2021</div>
|
||||
</div>
|
||||
<div className="card event-card-gold-border p-6">
|
||||
<blockquote className="section-text mb-4">
|
||||
"I love being part of such a vibrant and supportive community. The social events and activities have really enriched my life."
|
||||
</blockquote>
|
||||
<div className="card-title font-semibold">Maria J.</div>
|
||||
<div className="card-date text-sm">Member since 2018</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA */}
|
||||
<section className="py-16 cta-section">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
|
|
|
|||
|
|
@ -2,111 +2,31 @@
|
|||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import Hero from '../components/molecules/Hero';
|
||||
|
||||
// Featured Event Card Component
|
||||
interface EventCardProps {
|
||||
title: string;
|
||||
description: string;
|
||||
date: string;
|
||||
imageUrl?: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
const EventCard: React.FC<EventCardProps> = ({
|
||||
title,
|
||||
description,
|
||||
date,
|
||||
imageUrl,
|
||||
slug
|
||||
}) => {
|
||||
return (
|
||||
<div className="card event-card-gold-border h-full flex flex-col">
|
||||
<div className="card-image-placeholder aspect-video relative">
|
||||
{imageUrl ? (
|
||||
<Image
|
||||
src={imageUrl}
|
||||
alt={title}
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
className="object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<span className="card-placeholder-text">No image available</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="p-6 flex-grow flex flex-col">
|
||||
<h3 className="card-title event-card-title-gold text-xl font-semibold mb-2">{title}</h3>
|
||||
<p className="card-description mb-4 flex-grow">{description}</p>
|
||||
<div className="flex justify-between items-center mt-auto">
|
||||
<span className="card-date font-medium">{date}</span>
|
||||
<Link href={`/events/${slug}`} className="card-link hover:underline">
|
||||
Learn more
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
{/* Announcement Banner */}
|
||||
<div className="bg-color-bg-secondary border-b border-[var(--color-border)] py-2 px-4 text-center text-sm">
|
||||
<span className="font-semibold text-[var(--color-primary)]">Upcoming: </span>
|
||||
<span className="section-text">
|
||||
NOBDA & LAD Collaboration — A Special Book Reading with Franklin Jones on BASL,
|
||||
Saturday, May 23rd.
|
||||
</span>
|
||||
<Link href="/events" className="ml-2 card-link underline font-medium">Details →</Link>
|
||||
</div>
|
||||
|
||||
{/* Hero Section */}
|
||||
<Hero
|
||||
title="Louisiana Association of the Deaf"
|
||||
subtitle="The statewide organization serving the deaf and hard-of-hearing community in Louisiana since 1908."
|
||||
subtitle="The statewide organization serving the Deaf and hard-of-hearing community in Louisiana since 1908."
|
||||
primaryButtonText="Upcoming Events"
|
||||
primaryButtonLink="/events"
|
||||
secondaryButtonText="Join LAD"
|
||||
secondaryButtonLink="/membership"
|
||||
/>
|
||||
|
||||
{/* Featured events section */}
|
||||
{/* Events CTA section */}
|
||||
<section className="py-16 bg-color-bg">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="section-heading text-3xl font-bold mb-8 text-center">Featured Events</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<EventCard
|
||||
title="LAD 52nd Biennial State Conference"
|
||||
description="“Embracing the Challenges of Change” — Keynote by Dr. Natalie Delgado, 115th Anniversary Bash, comedian Sheena Lyles. Sheraton Metairie. $100 registration."
|
||||
date="June 1, 2023"
|
||||
slug="52nd-biennial-conference"
|
||||
/>
|
||||
<EventCard
|
||||
title="LSDF 19th Annual Dash for Deaf Kids 5K"
|
||||
description="LAD-sponsored annual 5K run/walk supporting the Louisiana School for the Deaf Foundation's programs for deaf children."
|
||||
date="Apr 15, 2023"
|
||||
slug="dash-for-deaf-kids-2023"
|
||||
/>
|
||||
<EventCard
|
||||
title="Monthly ASL Meet & Greet"
|
||||
description="Join us every last Thursday at Torchy's Tacos (except Nov/Dec) for socializing, networking, and ASL practice. All skill levels welcome."
|
||||
date="Last Thursday monthly"
|
||||
slug="asl-meet-and-greet"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center mt-10">
|
||||
<Link
|
||||
href="/events"
|
||||
className="btn-primary inline-block"
|
||||
>
|
||||
View All Events
|
||||
</Link>
|
||||
</div>
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="section-heading text-3xl font-bold mb-4">Upcoming Events</h2>
|
||||
<p className="section-text text-xl max-w-2xl mx-auto mb-8">
|
||||
From our monthly ASL Meet & Greet at Torchy's Tacos to our biennial statewide conference — there's always something happening in the Louisiana Deaf community.
|
||||
</p>
|
||||
<Link href="/events" className="btn-primary inline-block px-8 py-3 rounded-lg">
|
||||
View All Events
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -117,13 +37,13 @@ export default function Home() {
|
|||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||
<div>
|
||||
<p className="section-text text-lg mb-6">
|
||||
The Louisiana Association of the Deaf (LAD) is a non-profit statewide organization dedicated to serving the deaf and hard-of-hearing community across Louisiana.
|
||||
The Louisiana Association of the Deaf (LAD) is the oldest, largest, and premier nonprofit organization for the Deaf, DeafBlind, and hard of hearing (DDBHH) citizens in Louisiana. Founded in 1908 by alumni of the Louisiana School for the Deaf, LAD is one of fifty state affiliates of the National Association of the Deaf.
|
||||
</p>
|
||||
<p className="section-text text-lg mb-6">
|
||||
Our mission is to advocate for the rights, education, and well-being of deaf and hard-of-hearing individuals throughout Louisiana while fostering a strong sense of community.
|
||||
Our mission is to preserve, protect, and promote the civil, human, and linguistic rights of Deaf citizens of Louisiana — while fostering community, culture, and equal access for all DDBHH individuals.
|
||||
</p>
|
||||
<p className="section-text text-lg mb-6">
|
||||
Founded in 1908, LAD has over a century of advocacy and community service, working to advance opportunities and equal access for the deaf community.
|
||||
LAD also operates Deaf Focus, a Deaf-led services organization providing interpreting, ASL classes, advocacy, and community resources statewide.
|
||||
</p>
|
||||
<Link href="/about" className="btn-primary inline-block">
|
||||
Learn More About Us
|
||||
|
|
@ -159,7 +79,7 @@ export default function Home() {
|
|||
<div className="card event-card-gold-border p-6">
|
||||
<h3 className="benefit-card-title event-card-title-gold text-xl font-semibold mb-4">Advocacy Support</h3>
|
||||
<p className="benefit-card-description">
|
||||
Benefit from LAD's statewide advocacy work advancing the rights of deaf Louisianans.
|
||||
Benefit from LAD's statewide advocacy work advancing the rights of Deaf Louisianans.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -179,7 +99,7 @@ export default function Home() {
|
|||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="cta-title text-3xl font-bold mb-4">Join Our Community</h2>
|
||||
<p className="cta-description text-xl max-w-3xl mx-auto mb-8">
|
||||
Become a member today and connect with the deaf community across Louisiana.
|
||||
Become a member today and connect with the Deaf community across Louisiana.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<Link
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ export default function ThemeSamples() {
|
|||
<div className="blue-card-yellow-border p-6 rounded-lg">
|
||||
<h3 className="yellow-on-blue-title text-xl font-semibold mb-4">Support Network</h3>
|
||||
<p className="text-blue-100">
|
||||
Connect with a supportive network of deaf and hard-of-hearing individuals.
|
||||
Connect with a supportive network of Deaf and hard-of-hearing individuals.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -141,7 +141,7 @@ export default function ThemeSamples() {
|
|||
<div className="yellow-tint-card p-6 rounded-lg">
|
||||
<h3 className="text-xl font-semibold mb-4 text-yellow-800">Support Network</h3>
|
||||
<p className="text-yellow-700">
|
||||
Connect with a supportive network of deaf and hard-of-hearing individuals.
|
||||
Connect with a supportive network of Deaf and hard-of-hearing individuals.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
import { sanitizeHtml } from '@/utils/sanitize-html';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Updates | Louisiana Association of the Deaf',
|
||||
|
|
@ -68,21 +69,15 @@ export default async function UpdatesPage() {
|
|||
<h2 className="card-title event-card-title-gold text-xl font-semibold mb-3">
|
||||
{update.title}
|
||||
</h2>
|
||||
<p className="section-text">{update.body}</p>
|
||||
<div
|
||||
className="section-text prose prose-sm max-w-none"
|
||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(update.body ?? '') }}
|
||||
/>
|
||||
</article>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="max-w-4xl mx-auto mt-12 text-center">
|
||||
<p className="section-text text-sm">
|
||||
Showing recent highlights. Full update archive available to members.{' '}
|
||||
<Link href="/membership" className="card-link underline">
|
||||
Join LAD
|
||||
</Link>{' '}
|
||||
for complete access.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ const Hero: React.FC<HeroProps> = ({
|
|||
|
||||
return (
|
||||
<section
|
||||
className={`py-20 md:py-32 ${heroClass}`}
|
||||
className={`py-16 ${heroClass}`}
|
||||
style={backgroundStyle}
|
||||
aria-labelledby="hero-title"
|
||||
>
|
||||
|
|
@ -74,12 +74,13 @@ const Hero: React.FC<HeroProps> = ({
|
|||
<motion.div
|
||||
className="max-w-3xl mx-auto text-center"
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
initial={false}
|
||||
animate="visible"
|
||||
>
|
||||
<motion.h1
|
||||
id="hero-title"
|
||||
className="hero-title text-4xl md:text-5xl lg:text-6xl font-bold mb-6"
|
||||
initial={false}
|
||||
variants={itemVariants}
|
||||
>
|
||||
{title}
|
||||
|
|
@ -89,6 +90,7 @@ const Hero: React.FC<HeroProps> = ({
|
|||
className={`text-xl md:text-2xl mb-8 ${
|
||||
backgroundImageUrl ? 'hero-subtitle-image' : 'hero-subtitle'
|
||||
}`}
|
||||
initial={false}
|
||||
variants={itemVariants}
|
||||
>
|
||||
{subtitle}
|
||||
|
|
@ -96,6 +98,7 @@ const Hero: React.FC<HeroProps> = ({
|
|||
|
||||
<motion.div
|
||||
className="flex flex-col sm:flex-row justify-center gap-4"
|
||||
initial={false}
|
||||
variants={itemVariants}
|
||||
>
|
||||
<Link
|
||||
|
|
|
|||
|
|
@ -1,38 +1,30 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import ThemeSelector from '../molecules/ThemeSelector';
|
||||
|
||||
// Icons
|
||||
const MenuIcon = ({ className = '' }: { className?: string }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className={`w-6 h-6 ${className}`}
|
||||
>
|
||||
const MenuIcon = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CloseIcon = ({ className = '' }: { className?: string }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className={`w-6 h-6 ${className}`}
|
||||
>
|
||||
const CloseIcon = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const ChevronDown = ({ open }: { open: boolean }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor"
|
||||
className={`w-4 h-4 inline-block ml-1 transition-transform duration-200 ${open ? 'rotate-180' : ''}`}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
interface NavLinkProps {
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
|
|
@ -43,19 +35,11 @@ interface NavLinkProps {
|
|||
const NavLink = ({ href, children, isMobile = false, onClick }: NavLinkProps) => {
|
||||
const pathname = usePathname();
|
||||
const isActive = pathname === href;
|
||||
|
||||
const baseStyles = "transition-colors duration-200 px-2 py-2 rounded-md text-base font-medium";
|
||||
const mobileStyles = isMobile ? "block w-full text-left" : "";
|
||||
// Create a visually distinct style for active links using CSS variables
|
||||
const activeStyles = isActive
|
||||
? "nav-link-active"
|
||||
: "nav-link";
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={`${baseStyles} ${mobileStyles} ${activeStyles}`}
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={`transition-colors duration-200 px-3 py-2 rounded-md text-base font-medium ${isMobile ? 'block w-full text-left' : ''} ${isActive ? 'nav-link-active' : 'nav-link'}`}
|
||||
aria-current={isActive ? 'page' : undefined}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
|
|
@ -63,51 +47,61 @@ const NavLink = ({ href, children, isMobile = false, onClick }: NavLinkProps) =>
|
|||
);
|
||||
};
|
||||
|
||||
const getInvolvedLinks = [
|
||||
{ href: '/membership', label: 'Membership' },
|
||||
{ href: '/minutes', label: 'Minutes' },
|
||||
{ href: '/contact', label: 'Contact' },
|
||||
];
|
||||
|
||||
const Navigation = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const [mobileDropdownOpen, setMobileDropdownOpen] = useState(false);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const pathname = usePathname();
|
||||
|
||||
const toggleMenu = () => setIsOpen(!isOpen);
|
||||
const closeMenu = () => setIsOpen(false);
|
||||
const closeMenu = () => {
|
||||
setIsOpen(false);
|
||||
setMobileDropdownOpen(false);
|
||||
};
|
||||
|
||||
const dropdownActive = getInvolvedLinks.some(l => l.href === pathname);
|
||||
|
||||
// Handle scroll effect
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const offset = window.scrollY;
|
||||
if (offset > 50) {
|
||||
setScrolled(true);
|
||||
} else {
|
||||
setScrolled(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleScroll = () => setScrolled(window.scrollY > 50);
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
|
||||
// Close menu on ESC key
|
||||
useEffect(() => {
|
||||
const handleEsc = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEsc = (e: KeyboardEvent) => { if (e.key === 'Escape') { setIsOpen(false); setDropdownOpen(false); } };
|
||||
window.addEventListener('keydown', handleEsc);
|
||||
return () => window.removeEventListener('keydown', handleEsc);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
|
||||
setDropdownOpen(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
// Close dropdown on route change
|
||||
useEffect(() => { setDropdownOpen(false); }, [pathname]);
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={`
|
||||
fixed top-0 z-40 w-full nav-bar
|
||||
${scrolled ? 'nav-bar-scrolled' : ''}
|
||||
`}
|
||||
className={`fixed top-0 z-40 w-full nav-bar ${scrolled ? 'nav-bar-scrolled' : ''}`}
|
||||
aria-label="Main Navigation"
|
||||
>
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between h-16 items-center">
|
||||
{/* Logo and site name */}
|
||||
|
||||
{/* Logo */}
|
||||
<div className="flex-shrink-0">
|
||||
<Link href="/" className="flex items-center gap-2" onClick={closeMenu}>
|
||||
<Image
|
||||
|
|
@ -124,30 +118,58 @@ const Navigation = () => {
|
|||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Desktop navigation */}
|
||||
<div className="hidden md:block">
|
||||
<div className="ml-6 flex items-center space-x-1">
|
||||
<NavLink href="/">Home</NavLink>
|
||||
<NavLink href="/about">About</NavLink>
|
||||
<NavLink href="/updates">Updates</NavLink>
|
||||
<NavLink href="/events">Events</NavLink>
|
||||
<NavLink href="/minutes">Minutes</NavLink>
|
||||
<NavLink href="/deaf-focus">Deaf Focus</NavLink>
|
||||
<NavLink href="/membership">Membership</NavLink>
|
||||
<NavLink href="/contact">Contact</NavLink>
|
||||
<NavLink href="/donate">Donate</NavLink>
|
||||
<ThemeSelector />
|
||||
{/* Desktop nav */}
|
||||
<div className="hidden md:flex items-center space-x-1">
|
||||
<NavLink href="/about">About</NavLink>
|
||||
<NavLink href="/updates">Updates</NavLink>
|
||||
<NavLink href="/events">Events</NavLink>
|
||||
<NavLink href="/deaf-focus">Deaf Focus</NavLink>
|
||||
|
||||
{/* Get Involved dropdown */}
|
||||
<div className="relative" ref={dropdownRef}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setDropdownOpen(v => !v)}
|
||||
className={`transition-colors duration-200 px-3 py-2 rounded-md text-base font-medium flex items-center ${dropdownActive ? 'nav-link-active' : 'nav-link'}`}
|
||||
aria-expanded={dropdownOpen}
|
||||
aria-haspopup="true"
|
||||
>
|
||||
Get Involved <ChevronDown open={dropdownOpen} />
|
||||
</button>
|
||||
{dropdownOpen && (
|
||||
<div className="absolute right-0 mt-1 w-44 nav-dropdown rounded-md shadow-lg py-1 z-50">
|
||||
{getInvolvedLinks.map(l => (
|
||||
<Link
|
||||
key={l.href}
|
||||
href={l.href}
|
||||
className={`block px-4 py-2 text-sm nav-dropdown-item ${pathname === l.href ? 'nav-link-active' : ''}`}
|
||||
>
|
||||
{l.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Donate button */}
|
||||
<Link href="/donate" className="ml-2 btn-primary px-4 py-2 rounded-md text-sm font-semibold">
|
||||
Donate
|
||||
</Link>
|
||||
|
||||
<ThemeSelector />
|
||||
</div>
|
||||
|
||||
{/* Mobile menu button */}
|
||||
<div className="md:hidden">
|
||||
<div className="md:hidden flex items-center gap-2">
|
||||
<Link href="/donate" className="btn-primary px-3 py-1.5 rounded-md text-sm font-semibold">
|
||||
Donate
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="nav-mobile-button inline-flex items-center justify-center p-2 rounded-md focus:outline-none focus:ring-2 focus:ring-inset focus:ring-primary"
|
||||
aria-controls="mobile-menu"
|
||||
aria-expanded={isOpen}
|
||||
onClick={toggleMenu}
|
||||
onClick={() => setIsOpen(v => !v)}
|
||||
>
|
||||
<span className="sr-only">{isOpen ? 'Close main menu' : 'Open main menu'}</span>
|
||||
{isOpen ? <CloseIcon /> : <MenuIcon />}
|
||||
|
|
@ -156,21 +178,30 @@ const Navigation = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu, show/hide based on menu state */}
|
||||
<div
|
||||
className={`md:hidden ${isOpen ? 'block' : 'hidden'}`}
|
||||
id="mobile-menu"
|
||||
>
|
||||
{/* Mobile menu */}
|
||||
<div className={`md:hidden ${isOpen ? 'block' : 'hidden'}`} id="mobile-menu">
|
||||
<div className="nav-mobile-menu px-2 pt-2 pb-3 space-y-1">
|
||||
<NavLink href="/" isMobile onClick={closeMenu}>Home</NavLink>
|
||||
<NavLink href="/about" isMobile onClick={closeMenu}>About</NavLink>
|
||||
<NavLink href="/updates" isMobile onClick={closeMenu}>Updates</NavLink>
|
||||
<NavLink href="/events" isMobile onClick={closeMenu}>Events</NavLink>
|
||||
<NavLink href="/minutes" isMobile onClick={closeMenu}>Minutes</NavLink>
|
||||
<NavLink href="/deaf-focus" isMobile onClick={closeMenu}>Deaf Focus</NavLink>
|
||||
<NavLink href="/membership" isMobile onClick={closeMenu}>Membership</NavLink>
|
||||
<NavLink href="/contact" isMobile onClick={closeMenu}>Contact</NavLink>
|
||||
<NavLink href="/donate" isMobile onClick={closeMenu}>Donate</NavLink>
|
||||
|
||||
{/* Get Involved accordion */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMobileDropdownOpen(v => !v)}
|
||||
className={`w-full text-left transition-colors duration-200 px-3 py-2 rounded-md text-base font-medium flex items-center justify-between ${dropdownActive ? 'nav-link-active' : 'nav-link'}`}
|
||||
>
|
||||
Get Involved <ChevronDown open={mobileDropdownOpen} />
|
||||
</button>
|
||||
{mobileDropdownOpen && (
|
||||
<div className="pl-4 space-y-1">
|
||||
{getInvolvedLinks.map(l => (
|
||||
<NavLink key={l.href} href={l.href} isMobile onClick={closeMenu}>{l.label}</NavLink>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="px-3 py-2">
|
||||
<ThemeSelector />
|
||||
</div>
|
||||
|
|
|
|||
28
frontend/src/utils/sanitize-html.ts
Normal file
28
frontend/src/utils/sanitize-html.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
const ALLOWED_TAGS = new Set([
|
||||
'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
'ul', 'ol', 'li',
|
||||
'strong', 'em', 'b', 'i',
|
||||
'br', 'a', 'span', 'blockquote',
|
||||
]);
|
||||
|
||||
export function sanitizeHtml(html: string): string {
|
||||
if (!html) return '';
|
||||
return html
|
||||
.replace(/<(\/?)([a-zA-Z][a-zA-Z0-9]*)\b([^>]*)>/g, (_, close, tag, attrs) => {
|
||||
const t = tag.toLowerCase();
|
||||
if (!ALLOWED_TAGS.has(t)) return '';
|
||||
if (close) return `</${t}>`;
|
||||
if (t === 'a') {
|
||||
const m = attrs.match(/href="([^"]*)"/i);
|
||||
const href = m ? m[1] : null;
|
||||
if (!href) return '<a>';
|
||||
const safe = /^(https?:|mailto:|tel:|\/)/i.test(href) ? href : '#';
|
||||
return `<a href="${safe}" target="_blank" rel="noopener noreferrer">`;
|
||||
}
|
||||
if (t === 'br') return '<br>';
|
||||
return `<${t}>`;
|
||||
})
|
||||
// Strip empty tags that were only used for spacing
|
||||
.replace(/<([a-z][a-z0-9]*)\s*>\s*( | )?\s*<\/\1>/gi, '')
|
||||
.trim();
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue