UX Strategy — Conversion, Mobile, Accessibility
Events Discovery Page — UX Strategy & Conversion Optimization
Date: 2026-03-16 Status: Draft Scope: almaghrib.org events discovery page — static HTML+JS consuming public API Audience for this document: Engineering, product, and design stakeholders
Table of Contents
- Conversion Funnel Analysis
- Page Architecture & Information Hierarchy
- Social Proof & Urgency Patterns
- Mobile-First UX Strategy
- WhatsApp & Social Sharing Strategy
- SEO & Discoverability
- Accessibility Requirements
- Analytics & Measurement Plan
- Performance Budget
- Three Design Direction Concepts
1. Conversion Funnel Analysis
1.1 Entry Points & Optimization
Users arrive at this page from four primary channels, each with distinct intent and optimization needs:
| Entry Point | Est. Share | Intent | Optimization Strategy |
|---|---|---|---|
| WhatsApp share | 40-50% | "My friend sent me this event" — high intent for a specific event | Deep link directly to the event card pre-expanded. Pre-filter to the shared event's city. Minimize steps between landing and "Register." |
| Email newsletter | 25-30% | "What's new this month?" — browsing intent, moderate commitment | Landing with continent auto-detected via GeoIP. Show nearest events first. Subject line should match page headline for scent continuity. |
| Social media (Instagram, Facebook, X) | 15-20% | "This looks interesting" — low commitment, easily distracted | Strong hero with clear value proposition. Single compelling event showcased. Fast load time critical (social media users bounce in <3 seconds). |
| Direct / organic search | 5-10% | "AlMaghrib events near me" — high intent, self-directed | SEO-optimized title and meta. City name in URL if filtered. Structured data for Google Events rich results. |
Cross-channel optimization principles:
- Every entry point should resolve to a useful view within 1 second of interaction (no blank screens, no loading spinners on initial content)
- WhatsApp and email links should carry URL parameters that pre-set filters (continent, city, event ID)
- The page should feel complete and useful even before JavaScript hydrates (progressive enhancement)
1.2 Conversion Funnel Map
AWARENESS INTEREST DESIRE ACTION
─────────────────────────────────────────────────────────────────────────────────────────────────
Land on page ──► Scan events ──► Expand a card ──► Review details ──► Click "Register" ──► (off-site)
│ │ │ │ │
│ │ │ │ └─ Macro conversion
│ │ │ └─ Micro: scroll pricing, view instructor
│ │ └─ Micro: expand card
│ └─ Micro: use filter, scroll past fold
└─ Entry (tracked by source)
1.3 Key Conversion Metrics
| Metric | Definition | Target | Why It Matters |
|---|---|---|---|
| Registration click-through rate (CTR) | Clicks on "Register" / unique page visitors | 8-12% | Primary macro conversion. Users leave our page for almaghrib.org checkout. |
| Card expansion rate | Cards expanded / unique visitors | 35-50% | Indicates the summary view is compelling enough to drive curiosity. |
| Filter engagement rate | Visitors who use any filter / total visitors | 25-40% | Shows the navigation is discoverable and useful. Low rate means either auto-detection is perfect or filters are hidden. |
| Events-per-session | Average unique cards expanded per session | 1.5-2.5 | Multiple expansions signal browsing behavior — the page is serving discovery well. |
| Share rate | Share button clicks / unique visitors | 3-5% | Viral coefficient. Each share can bring 2-5 new visitors via WhatsApp group dynamics. |
| Bounce rate | Single-page sessions with no interaction / total sessions | < 40% | High bounce = wrong audience, slow load, or unclear value proposition. |
| Time to first interaction | Median time from page load to first click/tap | < 5 seconds | If users aren't interacting quickly, the page isn't immediately useful. |
1.4 Drop-Off Risks & Mitigations
| Funnel Stage | Risk | Severity | Mitigation |
|---|---|---|---|
| Page load | Slow load on mobile data (3G/4G) | HIGH | Performance budget: <2s first contentful paint. Skeleton screens. Defer non-critical images. |
| Page load | User sees events in wrong continent | HIGH | GeoIP auto-detection with instant filter. Fallback: "All" view sorted by proximity. |
| Scanning | Information overload (21+ events) | MEDIUM | Strong visual hierarchy. Continent grouping. Only 1-2 lines of text per card in summary. |
| Scanning | No events in user's city | MEDIUM | Show nearest city events with distance indicator. "No events in [city] yet — here's what's nearby." |
| Card expand | Card expansion feels heavy or slow | MEDIUM | CSS-only animation. No additional API calls. Content already loaded. |
| Detail review | Pricing is confusing (multi-tier) | HIGH | Clear pricing table. Bold the current/best price. Strikethrough for higher tiers. "You save $X" callout. |
| Detail review | No clear next step after reading | HIGH | Sticky "Register" CTA within expanded card. Repeat at top and bottom of detail view. |
| Register click | User leaves page (external link) | LOW | Expected behavior — but open in new tab so user can return. Track as conversion. |
| Register click | Fear of commitment / "I'll do it later" | MEDIUM | Add "Save" or "Share with friend" as soft CTAs. Calendar reminder link. |
1.5 Micro vs. Macro Conversions
Micro-conversions (engagement signals):
- Filter by continent
- Filter by city
- Expand event card
- Scroll within expanded card
- View instructor bio
- Click share button
- Scroll past 3+ events
Macro-conversions (business value):
- Click "Register Now" (primary)
- Share event via WhatsApp/social (secondary — drives new visitors)
- Click instructor link (tertiary — engagement with brand)
The key insight: On a discovery page with external registration, our macro conversion is the "Register" click. We cannot track actual registration completion — that happens on almaghrib.org. This means our analytics must be exceptionally precise about click-through, because it is our last measurable touchpoint.
2. Page Architecture & Information Hierarchy
2.1 Hero Section Strategy
The hero section must accomplish three things in under 3 seconds:
- Confirm identity — "Yes, this is AlMaghrib events"
- Communicate value — "Find an event near you"
- Enable action — "Start browsing now"
Recommended hero design:
┌──────────────────────────────────────────────────────────────────┐
│ [AlMaghrib Logo] [City: DC ▾]│
│ │
│ Find Your Next AlMaghrib Experience │
│ Weekend seminars, Ilm Nights, and more │
│ in 45+ cities worldwide │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ All │ │ N.America│ │ Europe │ │ Online │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ Ottawa Boston Calgary Charlotte Dallas Houston >>> │
└──────────────────────────────────────────────────────────────────┘
Hero messaging principles:
- Lead with the user's goal ("Find"), not the org's identity
- Include a concrete number ("45+ cities") for credibility
- Keep it to 2 lines maximum — the hero is a gateway, not a brochure
- No background image in the hero itself — let the event cards carry the visual weight. A subtle gradient or pattern (AlMaghrib purple-to-white) provides brand without competing with content
- The filter bar IS part of the hero — it should feel like a single integrated unit, not a separate component
What NOT to do in the hero:
- No carousel or rotating banners (they reduce conversion by 1-3% per Nielsen Norman Group)
- No "featured event" spotlight (creates editorial burden and ages quickly)
- No video or heavy animation (kills mobile performance)
- No generic stock imagery of mosques or calligraphy (users see through this instantly)
2.2 Filter Bar Design Principles
The filter bar is the primary navigation for this page. It must be:
Immediate — Filters apply instantly, no "Apply" button. Client-side filtering with ~21 events is instantaneous.
Visible — Sticky on scroll (desktop and mobile). The user should always be able to change their view without scrolling back up.
Progressive — Two levels that reveal progressively:
- Continent tabs — Always visible. 3-4 tabs maximum (All, North America, Europe, plus Online if included). Use a segmented control pattern, not traditional tabs.
- City pills — Appear below continent tabs. Horizontal scrollable row. Highlight the auto-detected city. Show event count per city as a badge.
Forgiving — "All" is always available and always the widest view. There is no dead-end state.
Filter bar specifics:
| Element | Desktop | Mobile |
|---|---|---|
| Continent tabs | Full-width segmented control, all visible | Full-width, same as desktop (3-4 tabs fit) |
| City pills | Horizontal row, wrapping to 2 rows if needed | Horizontal scroll with fade hint on right edge |
| Active state | Bold text + underline + background color change | Same, with slight haptic feedback feel |
| Event count | Badge on each city pill ("Dallas (3)") | Same |
| Sticky behavior | Sticks below site header on scroll | Sticks at top of viewport on scroll |
| Background | White with subtle bottom shadow when stuck | Same |
| Z-index | Above cards, below modals | Same |
City pill sorting: Sort by event count descending, then alphabetically. Most active cities appear first. This creates a natural "popularity" signal.
GeoIP auto-selection: On page load, auto-select the user's continent and highlight (but don't auto-filter to) their nearest city. The city pill should be visually highlighted ("Your area") but the view should show all cities in the continent. Reason: auto-filtering to a single city can show as few as 1 event, which feels empty. Showing the continent with the nearest city highlighted gives context while keeping the page full.
2.3 Card Design Principles for Scannability
Each card must be scannable in under 2 seconds. The user should be able to decide "Is this for me?" from the summary alone.
Card information hierarchy (summary view):
┌─────────────────────────────────────────────────┐
│ ┌──────────┐ │
│ │ │ Fiqh of Salah │
│ │ Poster │ with Sh. Yasir Qadhi │
│ │ Image │ │
│ │ 250x375 │ 📍 Houston, TX │
│ │ (cropped │ 📅 Apr 12-13 (Sat-Sun) │
│ │ to fit) │ │
│ └──────────┘ 🎓 2 credits │
│ │
│ ┌────────────┐ ┌──────────────────────┐ │
│ │ $99 Early │ │ 47 students enrolled │ │
│ │ Bird ✨ │ └──────────────────────┘ │
│ └────────────┘ │
│ [View ▾] │
└─────────────────────────────────────────────────┘
Summary card must include:
- Event image (poster 250x375, cropped/scaled) — visual anchor, aids recognition for returning visitors
- Title (heading) — largest text, bold, 1 line max with ellipsis
- Instructor — name only, small text, with tiny avatar if available (skip default placeholder avatars)
- Location — city + country (or state for US/Canada)
- Date — formatted as "Apr 12-13 (Sat-Sun)" — day-of-week is critical for weekend seminars
- Credits — small badge, only if > 0
- Price — show the lowest available price prominently ("From $99")
- Social proof — registration count if > 20 (hide if low to avoid negative social proof)
- Expand trigger — subtle "View Details" or chevron
What to EXCLUDE from summary (save for expanded view):
- Full description
- Venue details
- Pricing tiers breakdown
- Schedule details
- Sub-heading
- Long instructor bio
Card design rules:
- Consistent card height within a row (use CSS Grid, not Masonry)
- White card on light gray (#f6f8fa) page background
- Subtle border-radius (8px) and shadow (0 1px 3px rgba(0,0,0,0.1))
- No hover effects that reveal essential information (mobile has no hover)
- Pointer cursor on entire card (entire card is clickable to expand)
- Expand animation: 300ms ease-out, card grows in-place pushing siblings down
2.4 Expanded Detail View Strategy
When a card is expanded, it should feel like the same card opened up — not a new page or modal.
Recommended pattern: Inline expansion (accordion)
Reasons to prefer inline over modal:
- No context switch — user stays in the flow of browsing
- Better on mobile — modals on mobile often feel like new pages and disorient users
- Easier to compare events — other cards remain visible above/below
- Simpler implementation — no overlay management, scroll-lock, or escape handling
Expanded view layout:
┌──────────────────────────────────────────────────────────────────┐
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Background Image (960x600) │ │
│ │ with dark gradient overlay │ │
│ │ │ │
│ │ Fiqh of Salah │ │
│ │ Purification of the Soul Through Prayer │ │
│ │ with Sheikh Yasir Qadhi │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────────────────────┐ │
│ │ 📅 Dates │ │ Description │ │
│ │ Apr 12-13, 2026 │ │ Lorem ipsum dolor sit amet, │ │
│ │ Sat 10am-6pm │ │ consectetur adipiscing elit. │ │
│ │ Sun 10am-5pm │ │ Sed do eiusmod tempor... │ │
│ │ │ │ │ │
│ │ 📍 Venue │ │ [Read more] │ │
│ │ Marriott Houston │ │ │ │
│ │ 123 Main St │ └──────────────────────────────────┘ │
│ │ │ │
│ │ 🎓 2 Credits │ ┌──────────────────────────────────┐ │
│ │ │ │ Instructor │ │
│ │ 👥 47 enrolled │ │ [Avatar] Sheikh Yasir Qadhi │ │
│ └──────────────────┘ │ Brief bio line... │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Pricing │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Early Bird │ │ Standard │ │ Special │ │ │
│ │ │ $99 ✨ │ │ $129 │ │ Contact │ │ │
│ │ │ Until Apr 1│ │ │ │ │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ Register Now → │ │ Share 🔗 │ │ Collapse ▲ │ │
│ └──────────────────────┘ └──────────────┘ └────────────┘ │
└──────────────────────────────────────────────────────────────────┘
Expanded view must include:
- Background image (960x600) as a visual header within the expanded card
- Full title + subtitle (heading + sub_heading)
- Full description (truncated to ~3 lines with "Read more" for very long descriptions)
- Instructor section — avatar (if not placeholder), name, brief bio if available
- Date and schedule details — full date range with day-of-week and times with timezone
- Venue — name and address from
event_schedule_venue - Credits — with brief explanation of what credits mean
- Pricing breakdown — all tiers in a clear comparison layout
- Registration count — "47 students registered" with context
- Primary CTA — "Register Now" button, full-width on mobile, linking to
site_url - Secondary CTAs — Share button, "Add to Calendar" link
- Collapse trigger — clear way to close the expanded view
Scroll behavior on expand: When a card expands, smooth-scroll so the top of the expanded card is at the top of the viewport (accounting for sticky filter bar). This prevents the user from losing their place.
Only one card expanded at a time. Expanding a new card collapses the previously expanded one. Reason: performance on mobile, and it maintains a clear mental model.
2.5 Footer & Secondary Content
The footer should be minimal — this is a task-oriented page, not a content page.
Footer contents:
- AlMaghrib logo (small)
- Links: About AlMaghrib, Contact, Privacy Policy, Terms
- "Don't see your city? Request an event" — mailto or form link (community building)
- Social media links (Instagram, Facebook, YouTube, X)
- Copyright line
No newsletter signup in footer — the page's goal is event registration. Adding a secondary conversion dilutes focus.
"Don't see your city?" link is strategically important: it converts a dead-end (no events in my area) into a positive engagement signal and gives the org demand data for expansion planning.
3. Social Proof & Urgency Patterns
3.1 Registration Count Display
The total_attendees field is a powerful social proof signal, but it must be used carefully:
| Attendee Count | Display Strategy | Rationale |
|---|---|---|
| 0-9 | Hide entirely | Low numbers create negative social proof. "3 students registered" says "nobody wants this." |
| 10-29 | Show as "Students are registering" (no number) | Signals activity without exposing the low count. |
| 30-99 | Show exact number: "47 students registered" | Credible social proof. Large enough to be meaningful, specific enough to be believable. |
| 100-499 | Show rounded: "100+ students registered" | At this scale, precision doesn't matter. Round number feels bigger. |
| 500+ | Show with emphasis: "500+ students registered" with a visual highlight | This is a blockbuster event. Make it a badge or callout. |
Placement: In both summary card (small text near bottom) and expanded view (prominent position near pricing).
Formatting: Use the word "students" not "attendees" or "registrations" — "students" reinforces the educational identity and creates in-group belonging.
3.2 Pricing Urgency
Multi-tier pricing (early bird, standard, special) is a natural urgency driver. Use it explicitly:
On summary card:
- Show only the lowest available price: "From $99"
- If early bird is active, add a small badge: "Early Bird"
- If early bird has ended, show standard price with no special treatment
In expanded view:
- Show all pricing tiers in a comparison layout
- Visually emphasize the current best price (highlight, border, "Best Value" label)
- If early bird is active, show: "Early bird: $99 — Standard: $129" with the savings calculated: "Save $30"
- If early bird deadline is known, show countdown: "Early bird ends April 1" (not a ticking countdown timer, which feels aggressive for an educational brand)
- If early bird has passed, show a subtle strikethrough: "
$99 Early Bird$129 Standard" — this creates loss aversion for future events
Pricing display rules:
- Never show "from $0" or "Free" unless the event is genuinely free
- Currency should match the event's currency (the API provides currency-aware pricing)
- If the
specialtier exists, show it but label clearly (e.g., "Student/Senior: $79") - If pricing tiers are identical, show a single price (no fake comparison)
3.3 Instructor Reputation
Instructors are the primary draw for many AlMaghrib students. The instructor signal should be:
On summary card:
- Instructor name only, with small avatar (24x24px) if the avatar is not the default placeholder
- Detection: If the avatar URL contains "default" or "placeholder" or matches a known default path, skip it
- Name should be a subtle link to their profile on almaghrib.org (if available)
In expanded view:
- Larger avatar (48x48px or 64x64px) if available
- Full name with honorific (the API should provide this)
- 1-2 line bio if available, otherwise omit (do not show "No bio available")
- If the instructor has taught multiple events on the page, note it: "Also teaching [Event Name] in [City]"
Instructor as a filter (Phase 2 consideration):
- Some students follow specific instructors across cities
- A "Filter by instructor" option could serve this use case
- Low priority for v1 given the small event count (~21)
3.4 Credits as Value Signal
Credits (credits field) are meaningful to returning AlMaghrib students but opaque to newcomers.
Display strategy:
- Show as a small badge: "2 Credits" with a subtle info icon
- On hover/tap of the info icon: "AlMaghrib credits count toward your degree" (or similar concise explanation)
- Visually: use a graduation cap icon or academic-looking badge
- Only display when credits > 0
Positioning: Credits are a differentiator from competing Islamic education events. For returning students, it reinforces the unique value. For new students, it signals academic rigor without being intimidating.
3.5 Scarcity Indicators
Use scarcity sparingly and honestly. Manufactured scarcity destroys trust, especially in a faith-based community.
Appropriate scarcity signals:
- "Early bird pricing ends [date]" — real deadline, real savings
- "Limited to [X] seats" — only if the venue genuinely has a cap and the event is approaching it (e.g., >80% capacity)
- "Last event of the semester" — if it's genuinely the last one scheduled
Never use:
- Fake countdown timers
- "Only X spots left!" unless the data actually supports it (the API doesn't currently provide capacity data)
- "Selling fast!" without data backing it
- Red/orange alert colors for pricing (feels like a scam for an educational nonprofit)
Tone calibration: AlMaghrib's audience trusts the brand. Social proof and urgency should feel like helpful information ("here's what others are doing, here's how to save money") not like pressure ("act now or lose out!"). An Islamic education institute should feel trustworthy, not like a flash-sale site.
4. Mobile-First UX Strategy
4.1 Mobile Filter Design
Given 60-70% mobile traffic from WhatsApp and email, the filter experience must be thumb-native.
Recommended pattern: Persistent horizontal scroll (NOT bottom sheet or dropdown)
Rationale:
- Bottom sheets add a tap-to-open step. With only 3-4 continent tabs, they hide what should be visible.
- Dropdowns are slow on mobile and feel form-like, not navigation-like.
- Horizontal scroll tabs are the established mobile pattern (Google Maps, Yelp, Instagram Explore).
Mobile filter implementation:
┌─────────────────────────────────────────┐
│ [All] [N.America ✓] [Europe] [Online] │ ← Continent: full-width, all visible
├─────────────────────────────────────────┤
│ Ottawa Boston Calgary Dallas Hou >>>│ ← City: horizontal scroll, fade right
└─────────────────────────────────────────┘
Specific mobile filter rules:
- Continent tabs: Use full viewport width. 3-4 items will fit without scrolling on any phone. Equal-width segmented control.
- City pills: Horizontal scroll with a right-edge fade/gradient to hint at more options. Active city scrolls to center. Each pill shows event count as a small badge.
- Sticky: Both rows stick to the top on scroll, with a subtle shadow to indicate they're floating above content.
- Height: Filter bar should be no taller than 88px total (both rows) — ~10% of a mobile viewport.
- Tap targets: Each pill minimum 44x44px touch area (WCAG requirement).
Auto-scroll to nearest city: On first load, after GeoIP detection, auto-scroll the city pill row so the nearest city is visible (centered if possible). Add a subtle "Near you" label or location pin icon on the detected city.
4.2 Mobile Card Layout
Single-column, full-width cards:
┌─────────────────────────────────────┐
│ ┌─────────────────────────────┐ │
│ │ Poster Image │ │
│ │ (full-width, 16:9 crop) │ │
│ └─────────────────────────────┘ │
│ │
│ Fiqh of Salah │
│ with Sh. Yasir Qadhi │
│ │
│ 📍 Houston, TX · 📅 Apr 12-13 │
│ 🎓 2 credits · From $99 │
│ │
│ 47 students registered │
│ │
│ [View Details ▾] │
└─────────────────────────────────────┘
Mobile card specifics:
- Full-width card with 16px horizontal margin
- Image at top: use
image_bg(960x600) cropped to 16:9 for a wider, more immersive feel on mobile. This reverses the desktop recommendation (which uses the poster). The background image works better at full-width than the narrow poster format. - Title: 18px, bold, max 2 lines with ellipsis
- Meta info (location, date, credits, price) on 2 compact lines with dot separators
- Social proof: small text below meta
- Expand trigger: full-width "View Details" bar at card bottom
- Card gap: 12px between cards
Mobile expanded view:
- Expands in-place (no modal)
- Background image becomes a full-width hero within the card
- Pricing tiers stack vertically (1 per row) instead of side-by-side
- "Register Now" button: full-width, 48px height, sticky at bottom of expanded card viewport
- Share button: icon-only, positioned next to Register
- Collapse: "Show Less" at bottom, or tap the filter bar, or tap another card
4.3 Touch Targets & Gestures
| Element | Minimum Size | Gesture | Notes |
|---|---|---|---|
| Continent tab | 44x44px | Tap | Full-width segments, ample spacing |
| City pill | 44x36px min | Tap + horizontal swipe for scroll | Pill padding: 12px horizontal, 8px vertical |
| Event card (summary) | Full-width | Tap anywhere to expand | Entire card is a tap target |
| "Register Now" button | Full-width x 48px | Tap | Primary green/purple CTA color |
| Share button | 44x44px | Tap | Opens native share sheet on mobile |
| Collapse chevron | 44x44px | Tap | Large target, positioned in easy thumb zone |
| Card image | Full-width | None (no pinch-zoom needed) | Images are decorative, not informational |
Gestures to support:
- Pull-to-refresh: Refetches API data (in case new events were added)
- Swipe between cards: NOT recommended (breaks native scroll). Cards should be a simple vertical scroll.
- Back gesture (swipe from left edge): Should collapse expanded card if one is open, not navigate back in browser
4.4 Sticky Elements Strategy
Mobile viewport is precious. Be extremely selective about what sticks.
Sticky elements on mobile (max 2):
- Filter bar (continent tabs + city pills) — sticks at top. ~88px.
- "Register Now" button — sticks at bottom ONLY when an expanded card is in view. ~60px with padding.
NOT sticky:
- Header/logo (scrolls away — screen space is more valuable than branding)
- Search bar (Phase 2 feature, not needed with ~21 events)
- Share button (not important enough to stick)
- "Back to top" button (unnecessary with sticky filters)
Total sticky real estate: 88px (top) + 60px (bottom, conditional) = 148px max, leaving ~500px+ for content on a standard phone (667px viewport). This is within acceptable limits.
4.5 Performance Considerations for Mobile
| Concern | Strategy |
|---|---|
| Image loading | Lazy load all images below the fold. First 3 cards load eagerly. Use loading="lazy" attribute. |
| Image sizing | Serve poster (250x375) for card thumbnails, background (960x600) only when card is expanded. On mobile: use background image for summary too (cropped), but at reduced quality via CDN transform if available. |
| Initial payload | Single API call returns all ~21 events. Response is ~50-100KB JSON. This is acceptable even on 3G. |
| JavaScript | Minimal JS: fetch, filter, expand/collapse. No framework needed (vanilla JS or Preact at ~3KB). Target: <30KB JS total, gzipped. |
| CSS | Inline critical CSS in <head> for above-fold content. Remainder in a small external file. Target: <10KB CSS total. |
| Skeleton screens | Show 3 placeholder card outlines (gray rectangles) while API data loads. Shows structure immediately. |
| Font loading | Use system font stack (-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif) for body text. Only load a custom font (if needed) for headings, and use font-display: swap. |
| Touch responsiveness | No touch delay (use touch-action: manipulation on interactive elements). Instant visual feedback on tap (opacity change or background color shift). |
| Offline consideration | Not required for v1, but the page should show a graceful error if the API call fails: "Unable to load events. Please check your connection and try again." with a retry button. |
5. WhatsApp & Social Sharing Strategy
5.1 Open Graph Meta Tags Specification
Every shared link from this page must generate an attractive, informative preview card.
Default page meta (no specific event selected):
<meta property="og:type" content="website" />
<meta property="og:site_name" content="AlMaghrib Institute" />
<meta property="og:title" content="Upcoming Events — AlMaghrib Institute" />
<meta property="og:description" content="Browse weekend seminars, Ilm Nights, and more in 45+ cities. Find an event near you." />
<meta property="og:image" content="https://public.assets.almaghrib.org/events-page/og-default.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://almaghrib.org/events" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Upcoming Events — AlMaghrib Institute" />
<meta name="twitter:description" content="Browse weekend seminars, Ilm Nights, and more in 45+ cities." />
<meta name="twitter:image" content="https://public.assets.almaghrib.org/events-page/og-default.jpg" />
Per-event shared link meta (dynamically set via JS):
When a user shares a specific event (via the share button), the URL should include the event ID as a parameter. Since this is a static page, Open Graph tags cannot be truly dynamic server-side. Two approaches:
Option A (Recommended for v1): Server-side redirect page
Create a lightweight server-side endpoint (e.g., PHP, Node, or Cloudflare Worker) at /events/share/{event_id} that:
- Reads the event ID from the URL
- Fetches event data from the API
- Returns an HTML page with correct OG tags for that event
- Includes a JavaScript redirect to the main events page with the event pre-selected
This is the only way to get correct WhatsApp/social previews per event, because crawlers do not execute JavaScript.
Option B (Simpler but limited): Generic OG tags with smart URL
Use the default OG tags (above) for all shares. The share URL includes event parameters (?event=123&city=houston) so the page can pre-filter. The preview will always show the generic events page image, not the specific event.
Recommendation: Start with Option B, move to Option A if share-driven traffic becomes significant (>20% of total).
5.2 Share Button Placement & Design
Placement (two locations):
- Inside expanded card — Primary share location. Next to "Register Now" button. Users share after reading details.
- On summary card — Small share icon (top-right corner or bottom row). For quick sharing without expanding.
Design:
- Icon: Standard share icon (iOS share icon on Safari, generic share on other browsers)
- Label: "Share" on expanded view (icon + text), icon-only on summary card
- Behavior: Use
navigator.share()API on mobile (triggers native share sheet with WhatsApp, Messages, etc.). Fallback to "Copy link" with a confirmation tooltip on desktop.
The share button should NOT:
- Open a custom share modal with Facebook/Twitter/WhatsApp icons (feels dated, lower conversion than native share)
- Require the card to be expanded first (quick share from summary is valuable)
- Be hidden in a "..." menu (too many taps)
5.3 Deep Linking Strategy
URL parameter scheme:
https://almaghrib.org/events → Default view (all events, GeoIP auto-select)
https://almaghrib.org/events?continent=north-america → Pre-filtered to North America
https://almaghrib.org/events?city=houston → Pre-filtered to Houston
https://almaghrib.org/events?event=1234 → Scrolls to and auto-expands event 1234
https://almaghrib.org/events?city=houston&event=1234 → Filters to Houston, expands event 1234
https://almaghrib.org/events?type=weekend-seminars → Pre-filtered to weekend seminars (Phase 2)
Implementation rules:
- Parameters update the URL in real-time as the user filters (using
history.replaceState, notpushState— avoid polluting browser history with every filter click) - Page reads URL parameters on load and applies filters before rendering
- Use slug-style values (
north-america, notNorth Americaor1) - City slugs should match the API's city names, lowercased and hyphenated
- Event ID parameter should be the
event_idfrom the API
WhatsApp deep link construction: When a user shares via WhatsApp, the share should include:
- The URL with event and city parameters
- A pre-formatted text message (see below)
5.4 WhatsApp Share Message Format
WhatsApp is the primary sharing channel. The share message should be formatted for maximum click-through in a chat context.
Template for sharing a specific event:
📚 *{Event Title}*
🎓 with {Instructor Name}
📍 {City} — {Date Range}
💰 From {Lowest Price}
{X} students already registered!
🔗 {URL with event parameter}
Example:
📚 *Fiqh of Salah*
🎓 with Sh. Yasir Qadhi
📍 Houston, TX — Apr 12-13 (Sat-Sun)
💰 From $99 (Early Bird)
47 students already registered!
🔗 https://almaghrib.org/events?event=1234&city=houston
Implementation:
const shareText = `📚 *${event.heading}*\n🎓 with ${event.instructors[0]?.name}\n\n📍 ${event.city} — ${formatDateRange(event)}\n💰 From ${getLowestPrice(event)}\n\n${event.total_attendees > 20 ? event.total_attendees + ' students already registered!\n\n' : ''}🔗 ${shareUrl}`;
if (navigator.share) {
navigator.share({ text: shareText, url: shareUrl });
} else {
// Fallback: copy to clipboard
navigator.clipboard.writeText(shareText + '\n' + shareUrl);
}
WhatsApp-specific formatting notes:
*text*renders as bold in WhatsApp_text_renders as italic- Line breaks (
\n) create proper spacing - Emojis are fine — WhatsApp users expect them
- Keep it under 500 characters total (longer messages get truncated in previews)
- The URL should be on its own line for WhatsApp to generate a link preview
5.5 Social Preview Image
For the best WhatsApp/social preview, create a default OG image:
Specifications:
- Dimensions: 1200x630px (Facebook/WhatsApp standard)
- Content: AlMaghrib logo + "Find Your Next Event" text + a mosaic of 4-6 event poster images
- Style: Match the purple/gold/white brand palette
- File: JPEG, <300KB, hosted on the CDN
- Alternative: Generate per-event OG images using the event's
image_bgwith a text overlay (requires server-side rendering per Option A above)
6. SEO & Discoverability
6.1 Schema.org Event Structured Data
Add JSON-LD structured data for each event on the page. This enables Google Events rich results — a significant traffic driver for "islamic events near me" and similar queries.
Schema specification per event:
{
"@context": "https://schema.org",
"@type": "EducationEvent",
"name": "{heading}",
"description": "{description (first 200 chars)}",
"startDate": "{datetime_start in ISO 8601}",
"endDate": "{datetime_end in ISO 8601}",
"eventStatus": "https://schema.org/EventScheduled",
"eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
"location": {
"@type": "Place",
"name": "{event_schedule_venue name}",
"address": {
"@type": "PostalAddress",
"addressLocality": "{city}",
"addressCountry": "{country}"
}
},
"organizer": {
"@type": "Organization",
"name": "AlMaghrib Institute",
"url": "https://almaghrib.org"
},
"performer": [
{
"@type": "Person",
"name": "{instructors[0].name}",
"image": "{instructors[0].avatar}"
}
],
"image": "{image_bg}",
"offers": {
"@type": "AggregateOffer",
"lowPrice": "{lowest price}",
"highPrice": "{highest price}",
"priceCurrency": "{currency}",
"availability": "https://schema.org/InStock",
"url": "{site_url}"
},
"maximumAttendeeCapacity": null,
"remainingAttendeeCapacity": null
}
Implementation consideration: Since this is client-side rendered, Google may or may not execute the JavaScript to see the structured data. Two mitigations:
- Inject JSON-LD via JavaScript after data loads. Googlebot does render JavaScript for major sites. Since almaghrib.org is an established domain, this should work.
- Server-side render the JSON-LD (if available via WordPress template or Cloudflare Worker). This guarantees Googlebot sees the structured data regardless of JS execution.
Wrap all events in an ItemList schema:
{
"@context": "https://schema.org",
"@type": "ItemList",
"name": "Upcoming AlMaghrib Events",
"numberOfItems": 21,
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"item": { /* EducationEvent schema above */ }
}
]
}
6.2 Meta Tag Strategy
<title>Upcoming Islamic Events & Seminars — AlMaghrib Institute</title>
<meta name="description" content="Browse upcoming AlMaghrib weekend seminars, Ilm Nights, and community events in 45+ cities across North America and Europe. Find events near you and register today." />
<meta name="keywords" content="AlMaghrib, Islamic events, weekend seminars, Islamic education, Ilm Night, Muslim events" />
<link rel="canonical" href="https://almaghrib.org/events" />
<!-- Geo targeting -->
<meta name="geo.placename" content="Global" />
<!-- Pagination / filtering (for crawlers) -->
<link rel="alternate" href="https://almaghrib.org/events?continent=north-america" hreflang="en" />
<link rel="alternate" href="https://almaghrib.org/events?continent=europe" hreflang="en" />
6.3 URL Structure Recommendations
| URL | Purpose |
|---|---|
/events |
Main events page (canonical) |
/events?continent=north-america |
Filtered view (parameter, not separate URL) |
/events?city=houston |
City-filtered view |
/events?event=1234 |
Direct link to specific event |
/events/share/{event_id} |
Server-side share redirect (for OG tags, Phase 2) |
Why parameters, not path segments:
- The page is a single static page with client-side filtering. Creating separate URLs per city (/events/houston) would require either server-side routing or hash-based routing, both adding complexity.
- Google can crawl parameterized URLs and treats them as the same page with different states.
- Simpler implementation for a v1 static page.
Phase 2 consideration: If SEO becomes a priority, consider server-side rendering individual city pages (/events/houston) for better indexing and city-specific meta descriptions.
6.4 Internal Linking Strategy
- From the mega menu: Add a "View All Events" link in the In Person section that points to
/events - From
/weekend-seminars/: Add a banner or link: "See all events with details, pricing, and registration info" - From individual course pages: Link back to
/events?city={city}to show related events in the same city - From blog posts and content: When mentioning specific courses or cities, link to the filtered events view
- Sitemap: Include
/eventsin the XML sitemap withchangefreq=dailyand high priority
7. Accessibility Requirements
7.1 WCAG 2.1 AA Checklist for This Page
| Criterion | Requirement | Implementation |
|---|---|---|
| 1.1.1 Non-text Content | All images have alt text | Event images: alt="{event title} poster". Instructor avatars: alt="{instructor name}". Decorative images: alt="". |
| 1.3.1 Info and Relationships | Structure conveyed through markup | Filter tabs use role="tablist" and role="tab". Cards use <article>. Headings follow hierarchy (h1 for page title, h2 for event titles, h3 for sections within expanded cards). |
| 1.3.2 Meaningful Sequence | DOM order matches visual order | Cards render in the same order they appear visually. Filter bar before cards in DOM. |
| 1.4.1 Use of Color | Color not sole visual means | Active filter tab has bold text + underline + background change (not just color). Pricing urgency uses text labels, not just red/green. |
| 1.4.3 Contrast (Minimum) | 4.5:1 for normal text, 3:1 for large text | Body text on white: use #24292f (GitHub's color, 14.5:1 ratio). Muted text: #57606a minimum (5.7:1). Price text: #1a7f37 on white for green (4.8:1). |
| 1.4.4 Resize Text | Page usable at 200% zoom | Test at 200% — filter bar should not overflow, cards should reflow to single column. |
| 1.4.10 Reflow | No horizontal scroll at 320px width | Single-column layout at 320px. City pills are the only horizontal scroll element (by design, with proper scroll affordance). |
| 1.4.11 Non-text Contrast | UI components 3:1 against background | Card borders, button borders, filter tab outlines all meet 3:1 against background. |
| 2.1.1 Keyboard | All functionality available via keyboard | Tab through filters, Enter/Space to activate. Tab through cards, Enter to expand. Tab through expanded content. |
| 2.1.2 No Keyboard Trap | Keyboard focus never trapped | Expanding a card does not trap focus. Tab moves through expanded content and continues to next card. Escape collapses expanded card. |
| 2.4.3 Focus Order | Focus order matches logical sequence | Tab order: filters → cards (top to bottom) → footer. Within expanded card: image → details → pricing → Register → Share → Collapse. |
| 2.4.7 Focus Visible | Visible focus indicator on all interactive elements | Use a 2px solid outline in brand color (#7c3aed or similar purple) with 2px offset. Never use outline: none without a replacement. |
| 3.2.1 On Focus | No context change on focus alone | Focusing a filter tab does not activate it. Only Enter/Space activates. |
| 4.1.2 Name, Role, Value | Custom components have proper ARIA | Expandable cards use aria-expanded="true/false". Filter tabs use aria-selected. Registration counts use aria-label for screen reader context. |
7.2 Screen Reader Considerations
Filter tabs:
<div role="tablist" aria-label="Filter events by continent">
<button role="tab" aria-selected="true" aria-controls="events-panel" id="tab-all">
All Events
</button>
<button role="tab" aria-selected="false" aria-controls="events-panel" id="tab-na">
North America
</button>
<!-- ... -->
</div>
City pills (within the tab panel):
<div role="group" aria-label="Filter by city">
<button role="button" aria-pressed="false" aria-label="Houston, 3 events">
Houston (3)
</button>
<!-- ... -->
</div>
Expandable cards:
<article aria-label="Fiqh of Salah, April 12-13, Houston">
<div class="card-summary">
<h2>Fiqh of Salah</h2>
<!-- summary content -->
<button aria-expanded="false" aria-controls="event-1234-details">
View Details
</button>
</div>
<div id="event-1234-details" role="region" aria-labelledby="event-1234-title" hidden>
<!-- expanded content -->
</div>
</article>
Screen reader announcements:
- When a filter is activated: Announce "Showing X events in [Continent/City]" via
aria-live="polite"region - When a card is expanded: Focus moves to the first element in the expanded content
- When a card is collapsed: Focus returns to the expand/collapse button
Hidden live region for dynamic updates:
<div aria-live="polite" aria-atomic="true" class="sr-only" id="filter-status">
<!-- JS updates this: "Showing 8 events in North America" -->
</div>
7.3 Keyboard Navigation Flow
Tab order (sequential):
1. Skip to content link (hidden, appears on first Tab press)
2. Continent tabs (Left/Right arrow to navigate between tabs, Enter/Space to select)
3. City pills (Left/Right arrow to scroll, Enter/Space to select)
4. First event card summary
→ Enter/Space: expand card
→ Within expanded card: Tab through all interactive elements
→ Escape: collapse card, return focus to card trigger
5. Second event card summary
→ (repeat)
6. Footer links
Keyboard shortcuts (optional, Phase 2):
Escape— collapse any expanded cardHome— scroll to top and focus first filter/— focus search bar (if implemented)
7.4 Color Contrast Requirements
| Element | Foreground | Background | Ratio | Passes AA |
|---|---|---|---|---|
| Body text | #24292f | #ffffff | 14.5:1 | Yes |
| Muted/secondary text | #57606a | #ffffff | 5.7:1 | Yes |
| Card title | #24292f | #ffffff | 14.5:1 | Yes |
| Price text (green) | #1a7f37 | #ffffff | 4.8:1 | Yes |
| Active tab text | #ffffff | #7c3aed (purple) | 7.2:1 | Yes |
| Inactive tab text | #57606a | #f6f8fa (light gray) | 4.8:1 | Yes |
| Link text | #0969da | #ffffff | 4.7:1 | Yes |
| "Register" button text | #ffffff | #2da44e (green) or #7c3aed (purple) | 4.5:1+ | Yes |
| Card border | #d0d7de | #ffffff | 1.8:1 (decorative, 3:1 not required for borders used as visual grouping) | N/A |
| Focus outline | #7c3aed | any background | 3:1+ | Yes |
7.5 Focus Management
On page load:
- Focus starts at the top of the page (natural)
- If URL has an
?event=parameter, after the card auto-expands, focus moves to the expanded card's heading
On filter change:
- Focus stays on the filter that was activated (do not move focus to the first card — this is disorienting)
- Screen reader users hear the live region update ("Showing X events in [City]")
On card expand:
- Focus moves to the heading of the expanded content (first focusable element inside the detail region)
- The expand trigger button's
aria-expandedupdates to"true"
On card collapse:
- Focus returns to the expand/collapse trigger button
aria-expandedupdates to"false"
On "Register Now" click:
- Opens in new tab (
target="_blank"withrel="noopener noreferrer") - Focus stays on the current page (user can return via browser tab)
8. Analytics & Measurement Plan
8.1 Event Tracking Specification
All events should be tracked via a lightweight analytics library (Google Analytics 4, Plausible, or a custom endpoint). Use a consistent naming convention.
Page-level events:
| Event Name | Trigger | Properties |
|---|---|---|
page_view |
Page load complete | source (utm_source or referrer), continent_auto (GeoIP-detected), city_auto (GeoIP-detected), total_events (count of events loaded), load_time_ms |
page_error |
API call fails | error_type (network, timeout, parse), error_message |
Filter events:
| Event Name | Trigger | Properties |
|---|---|---|
filter_continent |
User clicks a continent tab | continent (slug), events_shown (count after filter), was_auto_selected (boolean) |
filter_city |
User clicks a city pill | city (slug), continent (slug), events_shown (count after filter), was_auto_selected (boolean) |
filter_reset |
User clicks "All" | previous_continent, previous_city |
Card events:
| Event Name | Trigger | Properties |
|---|---|---|
card_expand |
User expands a card | event_id, event_title, city, instructor, position (nth card on page), time_on_page_seconds |
card_collapse |
User collapses a card | event_id, time_expanded_seconds |
card_scroll |
User scrolls within expanded card | event_id, scroll_depth_percent (25, 50, 75, 100) |
Conversion events:
| Event Name | Trigger | Properties |
|---|---|---|
register_click |
User clicks "Register Now" | event_id, event_title, city, continent, instructor, price_shown (lowest tier), pricing_tier (early_bird / standard / special), attendees_shown (registration count), position, time_on_page_seconds, cards_expanded_count, source (utm_source) |
share_click |
User clicks share button | event_id, event_title, city, share_method (native_share / copy_link), share_location (summary_card / expanded_card) |
share_complete |
Native share completes (if detectable) | event_id, share_target (whatsapp / messages / etc., if available from share API) |
Engagement events:
| Event Name | Trigger | Properties |
|---|---|---|
instructor_click |
User clicks instructor name/link | instructor_name, event_id |
scroll_depth |
User scrolls to 25%, 50%, 75%, 100% of page | depth_percent, events_visible (how many cards were in view) |
time_on_page |
User has been on page for 15s, 30s, 60s, 120s | duration_seconds, cards_expanded, filters_used |
8.2 Heatmap & Scroll Depth Recommendations
Heatmap tracking (Hotjar, Microsoft Clarity, or similar):
- Record sessions on mobile and desktop separately (behavior differs significantly)
- Focus heatmap analysis on:
- Filter bar interaction (are users finding the filters? using them?)
- Card click patterns (where do users tap? top of card? image? expand button?)
- Expanded card scroll behavior (how far do users read in the detail view?)
- Register button clicks (do users reach it? is it below fold in expanded view?)
- Share button discovery (are users finding and using the share button?)
Scroll depth tracking:
- Track at 25% intervals for the overall page
- Track separately within expanded cards (do users scroll to see pricing? to see the Register button?)
- Hypothesis: Most users will not scroll past the 3rd screen of cards on mobile. If true, the GeoIP auto-detection is critical for showing relevant events first.
Session recording priorities:
- Mobile sessions from WhatsApp referrers (highest volume segment)
- Sessions where a user expanded a card but did NOT click Register (drop-off investigation)
- Sessions with 3+ card expansions (power users — what are they looking for?)
8.3 A/B Test Ideas for Future Optimization
| Test | Hypothesis | Metric | Priority |
|---|---|---|---|
| Card image: poster vs. background | The wider background image (960x600) on mobile card summaries will increase card expansion rate by 10%+ vs. the narrow poster (250x375) | Card expansion rate | HIGH |
| Social proof threshold | Showing "Students are registering" (no number) for events with 10-29 attendees increases Register CTR vs. hiding the count entirely | Register CTR for those events | HIGH |
| Pricing display: "From $99" vs. "$99 / $129" | Showing only the lowest price increases Register CTR, but showing both creates urgency via price anchoring | Register CTR | MEDIUM |
| Expand animation: inline vs. modal | Inline expansion has higher card expansion rate (lower friction) but modal may have higher Register CTR (more focused attention) | Card expansion rate + Register CTR | MEDIUM |
| Filter default: continent vs. city | Auto-selecting just the continent (showing all cities) vs. auto-selecting the nearest city. Broader view = more browsing, narrow view = faster conversion | Register CTR, events-per-session | MEDIUM |
| CTA copy: "Register Now" vs. "Learn More & Register" | "Register Now" is clearer but may feel like a commitment. "Learn More" implies the registration page has more information. | Register CTR | LOW |
| Hero: with stats vs. without | Adding "300K+ students worldwide" to the hero increases trust for new visitors | Bounce rate, Register CTR for first-time visitors | LOW |
| City pill sort: by count vs. alphabetical | Sorting by event count (Houston (3) before Boston (1)) vs. alphabetical. Count-based creates popularity signals but may feel unfamiliar. | Filter engagement rate | LOW |
8.4 Success Metrics & KPIs
Primary KPIs (review weekly):
| KPI | Target | Measurement |
|---|---|---|
| Register CTR | 8-12% | register_click / unique_visitors |
| Share rate | 3-5% | share_click / unique_visitors |
| Bounce rate | < 40% | Single-interaction sessions / total sessions |
| Mobile Register CTR | 6-10% | register_click on mobile / mobile unique_visitors |
Secondary KPIs (review monthly):
| KPI | Target | Measurement |
|---|---|---|
| Card expansion rate | 35-50% | card_expand (unique) / unique_visitors |
| Filter engagement | 25-40% | filter_continent or filter_city / unique_visitors |
| Events per session | 1.5-2.5 | card_expand (total) / sessions with at least 1 expand |
| Average time on page | 45-90 seconds | Median time_on_page for engaged sessions |
| WhatsApp share rate | 2-3% of total shares | share_complete where target=whatsapp / total share_click |
North star metric: Register CTR from mobile WhatsApp traffic. This is the highest-volume, highest-intent segment. If the page converts these users well, it is succeeding at its primary job.
Reporting cadence:
- Daily: Page views, Register CTR, top events by clicks (automated dashboard)
- Weekly: Full KPI review, funnel analysis, session recording review (5-10 sessions)
- Monthly: A/B test results, scroll depth analysis, share funnel analysis
9. Performance Budget
9.1 Target Load Times
| Metric | Target | Measurement |
|---|---|---|
| First Contentful Paint (FCP) | < 1.5s on 4G, < 3s on 3G | Lighthouse, Web Vitals |
| Largest Contentful Paint (LCP) | < 2.5s on 4G, < 4s on 3G | Lighthouse, Web Vitals |
| Cumulative Layout Shift (CLS) | < 0.1 | Lighthouse, Web Vitals |
| First Input Delay (FID) | < 100ms | Web Vitals |
| Interaction to Next Paint (INP) | < 200ms | Web Vitals |
| Time to Interactive (TTI) | < 3.5s on 4G | Lighthouse |
| Total page weight | < 500KB initial, < 1.5MB total (with lazy images) | DevTools Network |
9.2 Image Optimization Strategy
Image types and sizes:
| Image | Source Size | Optimized Target | Format | Loading |
|---|---|---|---|---|
| Event poster (card thumbnail) | 250x375 JPEG | 200x300 @ 80% quality | WebP with JPEG fallback | Eager (first 3), lazy (rest) |
| Event background (expanded) | 960x600 JPEG | 720x450 @ 75% quality | WebP with JPEG fallback | Lazy (only on expand) |
| Instructor avatar | Varies | 48x48 @ 80% quality | WebP with JPEG fallback | Lazy |
| OG share image | 1200x630 JPEG | 1200x630 @ 85% quality | JPEG (social crawlers need JPEG) | N/A (meta tag only) |
Implementation:
<!-- Card thumbnail with srcset -->
<picture>
<source
type="image/webp"
srcset="poster_200x300.webp 1x, poster_250x375.webp 2x"
/>
<img
src="poster_250x375.jpg"
alt="Fiqh of Salah poster"
width="200"
height="300"
loading="lazy"
decoding="async"
/>
</picture>
If the CDN does not support WebP or resizing on the fly:
- Use the images as-is from
public.assets.almaghrib.org - Set explicit
widthandheightattributes to prevent layout shift - Use
loading="lazy"for all images except the first 3 visible cards - Use
decoding="async"to avoid blocking the main thread - Consider a build step that downloads, converts, and hosts optimized versions (only worthwhile if the page is hosted on infrastructure you control)
Placeholder strategy:
- Use a solid purple (#7c3aed at 10% opacity) placeholder matching card dimensions
- The placeholder renders instantly (CSS background-color)
- When the image loads, it fades in with a 200ms transition
- This eliminates layout shift and feels polished
9.3 Critical CSS Strategy
Inline in <head> (critical CSS — ~5KB):
- Page background and layout grid
- Filter bar (continent tabs + city pills)
- Card summary layout (first 3 cards worth)
- Skeleton/placeholder styles
- Typography (font sizes, weights, colors for body and headings)
- Focus styles
Loaded asynchronously (non-critical CSS — ~5KB):
- Expanded card styles
- Footer styles
- Animation/transition definitions
- Print styles
- Scrollbar customization
Implementation:
<head>
<style>/* Critical CSS inlined here - ~5KB */</style>
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
</head>
9.4 JavaScript Bundle Size Target
| Component | Target Size (gzipped) | Notes |
|---|---|---|
| API fetch + data parsing | 2KB | Vanilla fetch, simple JSON parsing |
| Filter logic | 2KB | Client-side array filtering, URL parameter sync |
| Card expand/collapse | 2KB | DOM manipulation, ARIA updates, scroll behavior |
| Share functionality | 1KB | navigator.share + clipboard fallback + message formatting |
| GeoIP detection | 1KB | Single API call + distance calculation |
| Analytics wrapper | 2KB | Event dispatch to GA4 or custom endpoint |
| Schema.org injection | 1KB | JSON-LD generation from event data |
| Total | ~11KB gzipped | No framework needed |
Framework decision: At ~21 events with client-side filtering, vanilla JavaScript is more than sufficient. A framework (React, Vue, Preact) adds 3-30KB of overhead for negligible benefit at this scale. If the page later needs complex state management (e.g., real-time updates, user preferences, saved events), Preact (~3KB) would be the right minimal choice.
Third-party script budget:
- Analytics (GA4): ~30KB (loaded asynchronously, deferred)
- Heatmap (Clarity): ~20KB (loaded asynchronously, deferred)
- No other third-party scripts. No chat widgets, no marketing pixels, no font loaders on v1.
- Total third-party: ~50KB, all deferred, non-blocking
Loading order:
- HTML + critical CSS (inline) → First Contentful Paint
- API fetch begins (in parallel with CSS parse)
- Non-critical CSS loads asynchronously
- App JS loads (11KB) → parses API data, renders cards
- Images lazy-load as user scrolls
- Analytics + heatmap load deferred (after page interactive)
10. Three Design Direction Concepts
Direction A: "Community Hub"
Visual approach and mood: Warm, inviting, people-centric. The design emphasizes that events are about joining a community, not just attending a class. Colors are warm purples and golds with generous white space. Photography (where used) shows people — students, instructors, gatherings — not abstract Islamic geometric art. The vibe is "your friends are already going."
Hero section concept:
- Clean white background with a large headline: "Learn Together. Grow Together."
- Below the headline: "Join 300K+ students at AlMaghrib events worldwide"
- A subtle counter animation: "47 events this semester in 19 cities across 2 continents"
- No background image — the warmth comes from typography (a serif heading font like Lora or Playfair Display) and generous spacing
- The filter bar integrates seamlessly below the text, feeling like a natural continuation
Card design concept:
- Cards have rounded corners (12px) and warm shadows
- Instructor avatar is prominent — circular, 48px, positioned at the top-left overlapping the image slightly
- Registration count is shown with a "people" icon and warm phrasing: "47 students are going"
- Colors within the card: warm gray backgrounds (#faf9f7), deep purple for titles, gold accents for pricing
- Each card feels like a personal invitation
How filters are presented:
- Continent tabs are rounded pill buttons with a warm purple active state
- City pills are smaller, with a subtle people-count badge
- When a city is selected, a brief line appears: "3 events in Houston this semester"
- Filter bar has a very slight warm tint (#faf9f7) background when sticky
Mobile adaptation:
- Cards are full-width with generous padding (20px)
- Instructor avatar moves to an inline position (next to name, 32px)
- "X students are going" text is prominent — community proof is the primary mobile driver
- Register button uses warm purple (#7c3aed) instead of green — matches the community aesthetic
Strengths:
- Builds emotional connection and FOMO ("your community is doing this")
- Strong differentiation from competitor event pages (which tend to be cold/transactional)
- Social proof is front and center, which aligns with WhatsApp-driven traffic (friend recommended it)
- Feels distinctly AlMaghrib
Trade-offs:
- Slightly lower information density — requires more scroll to see the same number of events
- The warm aesthetic may feel less "professional" to some audiences
- Relies on instructor avatars being good quality (currently some are placeholders)
Direction B: "Clean Directory"
Visual approach and mood: Information-dense, scannable, efficient. Inspired by the clarity of Google search results, Stripe's documentation, or GitHub's issue lists. Every pixel serves a purpose. The design trusts users to know what they want and optimizes for fast scanning and filtering. No decorative elements. Neutral whites, grays, and blues with purple as the sole accent color.
Hero section concept:
- Minimal: AlMaghrib logo + "Events" as a single-line header
- The filter bar IS the hero — it sits prominently at the top with zero visual preamble
- No tagline, no stats, no value proposition — the events themselves are the value
- Total hero height: ~120px including filters
- This puts the first event card above the fold on all devices
Card design concept:
- Cards are compact rows (not boxes), similar to search results or email list items
- Each card is a single horizontal band: [Image (small square, 80x80)] [Title + Instructor + Meta] [Price + CTA]
- On desktop: all information in one line per card, with price right-aligned
- Borders between cards instead of card shadows — cleaner, denser
- No registration count on summary (shown in expanded view only)
- Credits shown as a tiny badge inline with the meta line
Layout:
┌──────────────────────────────────────────────────────────────────────┐
│ [Img] Fiqh of Salah · Sh. Yasir Qadhi · Houston · Apr 12-13 $99 →│
├──────────────────────────────────────────────────────────────────────┤
│ [Img] Heart Therapy · Sh. Omar Suleiman · Dallas · Apr 19-20 $109 →│
├──────────────────────────────────────────────────────────────────────┤
│ [Img] Light of Guidance · Dr. Reda Bedeir · NYC · Apr 26-27 $119 →│
└──────────────────────────────────────────────────────────────────────┘
How filters are presented:
- Continent tabs are minimal text links with an underline active state (no backgrounds, no pills)
- City pills are a compact horizontal row, smaller font size, more cities visible without scrolling
- A small "X events" counter updates in real-time next to the active filter
- Optional: add a search box inline with the filter bar for instant text filtering
Mobile adaptation:
- Cards switch from horizontal rows to compact vertical cards (image left, text right — a "list item" layout)
- Each card is shorter than Direction A — approximately 80px tall in summary
- More events visible per screen (5-6 on a phone vs. 2-3 in Direction A)
- Filter bar is a single row (continent tabs only), with city pills in a collapsible second row
Strengths:
- Maximum events visible per screen — users can scan 10+ events without scrolling on desktop
- Fastest time-to-decision for users who know what they want (city + date)
- Lowest cognitive load — no decorative elements competing for attention
- Easiest to maintain — no custom illustrations or photography needed
- Best for repeat visitors who come back every semester
Trade-offs:
- Less visually appealing for first-time visitors — may feel "plain" compared to the current AlMaghrib aesthetic
- Weaker emotional pull — doesn't communicate the AlMaghrib community experience
- Social proof is de-emphasized — registration counts hidden in expanded view
- May underperform for social media traffic (Instagram, WhatsApp) where visual appeal drives clicks
- The small images may not do justice to well-designed event posters
Direction C: "Immersive Showcase"
Visual approach and mood: Visual-first, cinematic, storytelling. Inspired by Apple's product pages, Airbnb's experience pages, or conference websites like WWDC. Large imagery, generous whitespace, and a sense of occasion. Each event feels like a significant experience worth investing in, not just a class listing. The design conveys prestige and quality.
Hero section concept:
- Full-viewport hero with a large background image (curated photo of an AlMaghrib event in session — students engaged, instructor teaching, emotional moment)
- Semi-transparent dark overlay with large white text: "Where Knowledge Comes Alive"
- Subtle floating filter bar overlaid on the hero image (transparent background, white text)
- As user scrolls, the hero parallax-scrolls away and the filter bar becomes sticky with a solid white background
- Total hero: 100vh (full viewport) on desktop, 60vh on mobile
Card design concept:
- Cards are large tiles with the background image (960x600) as the dominant element
- Text overlaid on the bottom third of the image with a dark gradient
- Each card is roughly 16:9 aspect ratio — very visual, almost like movie posters
- On hover: the image zooms slightly (1.03 scale), and additional info fades in
- Registration count styled as a "badge" floating on the image: "47 Going"
- Desktop: 2-column grid (large cards). Mobile: full-width single column.
Layout:
┌────────────────────────────────┐ ┌────────────────────────────────┐
│ │ │ │
│ [960x600 Background] │ │ [960x600 Background] │
│ │ │ │
│ │ │ │
│ ┌──────────────────────────┐ │ │ ┌──────────────────────────┐ │
│ │ Fiqh of Salah │ │ │ │ Heart Therapy │ │
│ │ Sh. Yasir Qadhi │ │ │ │ Sh. Omar Suleiman │ │
│ │ Houston · Apr 12-13 · $99│ │ │ │ Dallas · Apr 19-20 · $109│ │
│ └──────────────────────────┘ │ │ └──────────────────────────┘ │
└────────────────────────────────┘ └────────────────────────────────┘
How filters are presented:
- Filters are a floating bar that sits on top of the hero image initially
- Continent tabs are large, spaced-out text buttons
- City pills slide in from the right when a continent is selected, with a subtle animation
- The filter interaction feels premium — smooth transitions, deliberate animations
- When sticky: the bar becomes a clean white strip with minimal height
Mobile adaptation:
- Hero shrinks to 50vh but maintains the cinematic feel
- Cards are full-bleed (edge to edge, no margins), each taking roughly 60% of viewport height
- Swiping through cards feels like browsing a gallery
- The "Register" button appears as a floating pill at the bottom of the screen when an event card is centered
- Filters collapse to a compact bar with city name visible ("Houston ▾") — tap to expand into a full-screen filter overlay
Strengths:
- Strongest first impression — creates a "wow" moment that differentiates from every other Islamic event page
- Events feel premium and worth the investment — justifies the pricing
- Best for social media sharing — screenshots of this page will look stunning on Instagram/WhatsApp
- Instructor avatars and event imagery get maximum visual impact
- Creates a sense of occasion — "this is not just a class, this is an experience"
Trade-offs:
- Heaviest on performance — large images, parallax effects, and animations cost bytes and CPU time
- Lowest information density — users see only 2 events per screen on desktop, 1 on mobile
- Scanning is slow — not ideal for users who want to quickly compare 5+ events
- Parallax and hover effects may not work well on all mobile devices (especially older Android)
- Requires high-quality imagery for every event — events with weak posters will look bad at this scale
- The full-viewport hero may feel like an obstacle for repeat visitors who just want to see events
- Most complex to implement and maintain — CSS animations, intersection observers, scroll position tracking
- Accessibility concerns with text overlaid on images (contrast varies by image)
Design Direction Recommendation
For v1: Direction B ("Clean Directory") with selected elements from Direction A ("Community Hub").
Rationale:
- Performance first. With 60-70% mobile WhatsApp traffic, load speed and scannability are non-negotiable. Direction B is the lightest and fastest.
- Community trust already exists. Users arriving from WhatsApp shares already have social proof (their friend sent it). The page doesn't need to sell the AlMaghrib brand — it needs to get users to the right event and registered.
- 21 events is a small catalog. With this few events, the page's job is to help users filter to their city and review details quickly. A directory pattern serves this better than a showcase.
- Selective community warmth. Borrow from Direction A: show "X students registered" on cards (when count > 30), use the warm purple palette, and add the instructor avatar on expanded view. These add community feel without sacrificing density.
- Direction C for later. As the event catalog grows and the page becomes a primary landing page (rather than a WhatsApp referral destination), a more visual approach makes sense. The immersive showcase would be powerful for social media marketing campaigns.
v1 implementation priority:
- Direction B layout (compact, efficient, information-dense)
- Direction A warmth (purple palette, friendly copy, social proof)
- Direction C imagery (use background images only in expanded view, not summary cards)
Appendix A: Implementation Checklist
A phased checklist for building the page against this strategy:
Phase 1 — Core Page (Week 1-2)
- HTML scaffold with semantic markup and ARIA roles
- Critical CSS inlined, non-critical CSS deferred
- API fetch with loading skeleton and error state
- Continent filter tabs (keyboard navigable, screen reader announced)
- City pill filter (horizontal scroll on mobile)
- Card summary rendering (title, instructor, location, date, price)
- Card expand/collapse with focus management
- Expanded view with full details and pricing
- "Register Now" button linking to
site_url - URL parameter sync (continent, city, event)
- GeoIP auto-detection for default filter
- Responsive layout (1 column mobile, 2 tablet, 3 desktop)
Phase 2 — Polish (Week 2-3)
- Image lazy loading with placeholders
- Social proof display (registration count with thresholds)
- Pricing urgency display (early bird callout)
- Share button with
navigator.share()and clipboard fallback - WhatsApp share message formatting
- Schema.org JSON-LD structured data
- Open Graph and Twitter Card meta tags
- Analytics event tracking (all events in Section 8)
- Performance audit (Lighthouse > 90 on mobile)
- Accessibility audit (axe-core, keyboard walkthrough)
- Cross-browser testing (Safari, Chrome, Firefox, Samsung Internet)
Phase 3 — Enhancement (Week 3-4)
- Event type filtering (Weekend Seminars, Ilm Nights)
- Sort options (date, distance)
- "Don't see your city?" footer link
- Sticky filter bar shadow on scroll
- Print stylesheet
- Heatmap and session recording setup
- A/B test framework for card image format (poster vs. background)
- Per-event OG images via server-side share endpoint (if prioritized)
Appendix B: Reference Comparisons
Pages to study for pattern inspiration (not copy):
| Site | What to Study | Relevant Pattern |
|---|---|---|
| Eventbrite search results | Card density, filter UX, mobile layout | Directory pattern, tag-based filtering |
| Airbnb Experiences | Card design, image treatment, social proof | Visual cards with price, rating, host info |
| Google Events | Structured data display, search intent matching | Schema.org integration, location-based results |
| ClassPass | Studio/class discovery, booking CTA placement | Mobile-first class cards, sticky CTA |
| Meetup.com | Community event discovery, city-based filtering | Group-based events, location auto-detect |
| Coursera course catalog | Educational course cards, instructor prominence | Credits/certification badge pattern |
This document should be revisited after the first 2 weeks of page analytics data to validate assumptions and adjust the strategy based on actual user behavior.