/ — public.
Replaces both:
codeforphilly.org/(the hand-curated marketing landing)- The laddr
home.tplactivity feed + meetups sidebar
This v1 page is a blend: marketing hero + featured projects + live activity. The hard-coded portfolio from codeforphilly.org/html-templates/home.tpl is replaced by a curated-from-data approach driven by a small featured flag.
GET /api/projects?featured=true&perPage=6— featured project list (Project list items)GET /api/projects?sort=-activity&perPage=12— the most active projects, to top up the featured listfeaturedis a new boolean column onprojects(add to data-model.md). Staff toggles it.
GET /api/events?when=upcoming&perPage=3— the upcoming-events stripGET /api/project-updates?perPage=10— recent activity for the activity streamGET /api/help-wanted?perPage=4&sort=-createdAt— recent open roles for the "How to help" railGET /api/auth/me— bootstraps the auth context (changes header CTA)
The page is composed of four sections, top to bottom:
- Site logo, headline, subhead
- Headline: "Contribute towards technology-related projects that benefit the City of Philadelphia."
- Subhead: "No coding experience required."
- Primary CTA: "Volunteer" (links to
/volunteer) - Background: a Ken Burns photo slideshow rendered by
<HeroSlideshow />. Photos live inapps/web/public/hero/as optimized JPG (primary) + WebP (via<picture>), with amanifest.jsonindex. On mount the photo set is shuffled; each photo is displayed for 8 seconds with a slow ambient pan (scale 1.05 → 1.10, translate ±2% on a randomized vector), then crossfades to the next over 1.5 seconds while the incoming photo immediately starts its own independent random pan. Theprefers-reduced-motionmedia query disables the pan entirely; the crossfade still occurs. A dark gradient overlay sits above the photos to keep hero text legible. Asset regeneration is reproducible viaapps/web/scripts/optimize-hero-photos.sh <input-dir>. - If the user is signed in, the primary CTA changes to "Browse Projects" (
/projects).
A slim strip directly under the hero. Rules in behaviors/events.md; data from GET /api/events?when=upcoming&perPage=3.
- Heading: "Upcoming Events"
- Up to 3 events, soonest first, each as a compact row: date block (weekday, day, month in
America/New_York), title linked to/events/<slug>, time range,venueName(or "Online"), and "14 going" — or "You're going ✓" when the viewer has an RSVP - The soonest event's row carries an inline "RSVP" button (anonymous: "Sign in to RSVP") so the next hack night is one click from the home page; the other rows link only
- Cancelled events render with a "Cancelled" badge and no button
- "See all events →" links to
/events; "Subscribe" links to thewebcal://feed
When there are no upcoming events the strip is hidden entirely (unlike the featured grid, which stays as an empty heading). An empty "Upcoming Events" box on the home page would advertise that nothing is happening.
- Heading: "Join a Project"
- Grid: responsive — 3 columns ≥ md, 2 columns ≥ sm, 1 column < sm
- Six tiles: the staff-featured projects first (in their list order), then the most active projects (
sort=-activity) not already shown, until there are six. A curated pick always wins a slot; the activity fill means the section is never empty just because nobody has curated it. Each tile shows:- Project image —
project.featuredImageKeywhen set (required whenproject.featured = true; see data-model.md#project); otherwise no image area at all, since a grid of blank placeholder panels reads as a dead page - Stage badge
- Title
- Tagline =
project.summary, or first line ofoverviewExcerptifsummaryis null - Click: links to
/projects/<slug>
- Project image —
- Below the grid: "Browse all 268 projects →" link to
/projects, where the list can be searched and filtered ("Browse all projects →" until the count loads). The count ismetadata.totalItemsfrom a separate cheapHEAD-style call (or piggybacked from the featured response — implementer's call).
Three side-by-side cards. Slack comes first: it's where a newcomer actually meets people. Sponsoring stays one click away in the footer and the About menu.
| Card | Heading | Body | Link |
|---|---|---|---|
| Slack | "Join us on Slack" | "Say hello in #general and meet the community" | /chat (a full-page navigation — chat.md) |
| Start a Project | "Start a Project" | "Start or get help on a project" | /projects/create for signed-in users, else /login?return=/projects/create |
| Volunteer | "Volunteer" | "Join our projects" | /volunteer |
- Heading: "Latest Project Activity"
- Below heading, three filter buttons (chip-style): All / Updates / Buzz — default All
- List of activity cards, newest first, 10 items, with "View all activity →" linking to
/project-updates - Each card is either a ProjectUpdate or a ProjectBuzz; rendering rules in behaviors/activity-feed.md
- Sidebar on the right at ≥ lg breakpoint, collapses below the activity stream at smaller breakpoints
- Heading: "Help Wanted"
- Up to 4 recent open
HelpWantedRolecards. Each card shows:- Title
- Project (link to project page)
commitmentHoursPerWeekif set, e.g., "~4 hrs/week"- Tags (small chips)
- "Browse all open roles →" links to
/help-wanted
| Action | Affects | Caused by |
|---|---|---|
| Hero CTA click | Navigation | "Volunteer" or "Browse Projects" button |
| Featured tile click | Navigation | Tile or title |
| Get-involved card click | Navigation | Whole card is clickable |
| Activity card click | Navigation | Card body links to project; "by " links to person |
| Help-wanted card click | Navigation | Whole card links to project page, anchored to that role |
| Upcoming event row click | Navigation to /events/<slug> |
Row |
| RSVP to the next event | PUT /api/events/:slug/rsvp in place; row flips to "You're going ✓" |
Inline button on the soonest event |
| Filter chip click | Local filter of activity stream | No re-fetch in v1 — client-side filter of the loaded 10 |
To here:
- Root URL
/ - Logo / "home" link in app header from every other page
From here:
/volunteer,/sponsor,/projects,/projects/<slug>,/members/<slug>,/project-updates,/project-buzz,/help-wanted,/events,/events/<slug>,/login
| Section | Anonymous | User |
|---|---|---|
| Hero CTA | "Volunteer" | "Browse Projects" |
| Featured projects | visible | visible |
| Get involved | visible — "Start a Project" routes through /login?return=/projects/create |
visible — "Start a Project" links to the in-app /projects/create form |
| Activity stream | visible | visible |
| Help-wanted rail | visible | visible — "Express interest" CTA on each card (deferred polish; v1 just links to project page) |
There are no staff- or admin-only home sections.
This is the first impression. Inline-critical CSS for the hero; lazy-load below-fold sections; defer the activity stream and help-wanted rail to a post-FCP fetch.