Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
dd34602
chore(plans): add site-check-153 plan
heyoub Aug 24, 2026
916ef65
docs(specs): reorder header clusters, repoint dead GitBook links
heyoub Aug 24, 2026
5623b5a
fix(web): move Volunteer CTA rightmost, pad the mobile sheet
heyoub Aug 24, 2026
a768f87
fix(web): repoint dead GitBook links and the stale footer repo URL
heyoub Aug 24, 2026
3ca3d95
fix(web): let the mobile sheet nav scroll when it overflows
heyoub Aug 24, 2026
54f9745
chore(plans): record site-check-153 validation results
heyoub Aug 24, 2026
5729702
chore(plans): record exact api test counts in site-check-153 notes
heyoub Aug 24, 2026
bb05e0f
chore(plans): record browser validation for site-check-153
heyoub Aug 24, 2026
e092de3
chore(plans): mark site-check-153 done (PR #154)
heyoub Aug 24, 2026
eb1cbc3
docs: repoint stale codeforphilly-rewrite GitHub URLs
themightychris Sep 9, 2026
12dea00
chore(web): remove dead HomeStub page
themightychris Sep 9, 2026
9201cbe
test(web): settle AuthProvider before Volunteer tests return
themightychris Sep 9, 2026
bebd034
refactor(web): lift GitHubIcon into a shared component
themightychris Sep 9, 2026
ffdc5e9
fix(web): close the mobile sheet on any navigation
themightychris Sep 9, 2026
6cdccd1
fix(web): describe the sheet dialog, drop its dead width class
themightychris Sep 9, 2026
b417c87
fix(web): wrap the utility cluster in a navigation landmark
themightychris Sep 9, 2026
d5a4221
fix(web): keep the header on one line between md and lg
themightychris Sep 9, 2026
684ca34
chore(plans): record review closeout for site-check-153
themightychris Sep 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/src/components/AppFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export function AppFooter() {
Copyright © Code for Philly {FOUNDED_YEAR}–{CURRENT_YEAR}
</p>
<a
href="https://github.com/CodeForPhilly/codeforphilly-rewrite"
href="https://github.com/CodeForPhilly/codeforphilly-ng"
target="_blank"
rel="noopener noreferrer"
className="hover:text-foreground transition-colors"
Expand Down
182 changes: 102 additions & 80 deletions apps/web/src/components/AppHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { Link, NavLink } from 'react-router';
import { Link, NavLink, useLocation } from 'react-router';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
Expand All @@ -8,11 +8,21 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet';
import { Separator } from '@/components/ui/separator';
import { SearchBox } from '@/components/SearchBox';
import { GitHubIcon } from '@/components/icons/GitHubIcon';
import { useAuth } from '@/hooks/useAuth';

const GITHUB_URL = 'https://github.com/CodeForPhilly';

function ChevronDownIcon() {
return (
<svg
Expand Down Expand Up @@ -60,7 +70,7 @@ function AuthControls({ mobile = false }: { mobile?: boolean }) {
return (
<div
className={`h-8 ${mobile ? 'w-full' : 'w-20'} bg-muted animate-pulse rounded`}
aria-label="Loading auth state"
aria-hidden="true"
/>
);
}
Expand Down Expand Up @@ -143,12 +153,8 @@ function AboutDropdown() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className="flex items-center gap-1"
aria-label="About menu"
>
{/* No aria-label: the visible "About" text is the accessible name. */}
<Button variant="ghost" size="sm" className="flex items-center gap-1">
About <ChevronDownIcon />
</Button>
</DropdownMenuTrigger>
Expand Down Expand Up @@ -181,8 +187,34 @@ const navLinkClass = ({ isActive }: { isActive: boolean }) =>
isActive ? 'text-primary' : 'text-muted-foreground'
}`;

function GitHubLink() {
return (
// Desktop-only: between md and lg the header has no room for it (the
// utility cluster would push "Help Wanted" onto two lines); the mobile
// sheet carries its own GitHub row.
<Button asChild variant="ghost" size="icon-sm" className="hidden lg:inline-flex">
<a
href={GITHUB_URL}
target="_blank"
rel="noopener noreferrer"
aria-label="Code for Philly on GitHub"
>
<GitHubIcon />
</a>
</Button>
);
}

export function AppHeader() {
const [mobileOpen, setMobileOpen] = useState(false);
const location = useLocation();
// The sheet is open only for the location it was opened at, so any
// client-side navigation — a NavLink or Enter in the inline search —
// closes it without per-item onClick closers. Derived during render
// rather than synced in an effect (react-hooks/set-state-in-effect).
const [openedAtKey, setOpenedAtKey] = useState<string | null>(null);
const mobileOpen = openedAtKey === location.key;
const setMobileOpen = (open: boolean) =>
setOpenedAtKey(open ? location.key : null);

return (
<header className="sticky top-0 z-40 w-full border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 shadow-sm print:hidden">
Expand All @@ -201,135 +233,125 @@ export function AppHeader() {
/>
</Link>

{/* Desktop nav */}
{/* Desktop content cluster. The parent gap is the only source of
spacing between children — no per-child margins. */}
<nav
aria-label="Primary navigation"
className="hidden md:flex items-center gap-1 ml-4 flex-1"
className="hidden md:flex items-center gap-2 ml-4 flex-1"
>
<NavLink to="/projects" className={navLinkClass}>
Projects
</NavLink>
<NavLink
to="/help-wanted"
className={({ isActive }) => navLinkClass({ isActive }) + ' ml-1'}
>
<NavLink to="/help-wanted" className={navLinkClass}>
Help Wanted
</NavLink>
<NavLink
to="/members"
className={({ isActive }) => navLinkClass({ isActive }) + ' ml-1'}
>
<NavLink to="/members" className={navLinkClass}>
Members
</NavLink>
<Button asChild size="sm" className="ml-1 bg-green-600 hover:bg-green-700 text-white">
<NavLink to="/volunteer">Volunteer</NavLink>
</Button>
<div className="ml-1">
<AboutDropdown />
</div>
<AboutDropdown />
</nav>

{/* Desktop: search + auth */}
<div className="hidden md:flex items-center gap-2 ml-auto">
{/* Desktop utility cluster: GitHub, search, auth, then the Volunteer
CTA pinned rightmost (specs/behaviors/app-shell.md). */}
<nav
aria-label="Utility"
className="hidden md:flex items-center gap-2 ml-auto"
>
<GitHubLink />
<SearchBox />
<AuthControls />
</div>
<Button
asChild
size="sm"
className="bg-green-600 hover:bg-green-700 text-white"
>
<NavLink to="/volunteer">Volunteer</NavLink>
</Button>
</nav>

{/* Mobile: auth + hamburger */}
<div className="flex md:hidden items-center gap-2 ml-auto">
<AuthControls />
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
<SheetTrigger asChild>
{/* No aria-expanded here — Radix's Dialog.Trigger supplies it. */}
<Button
variant="ghost"
size="sm"
aria-label="Open navigation menu"
aria-expanded={mobileOpen}
>
<MenuIcon />
</Button>
</SheetTrigger>
<SheetContent side="right" className="w-72 flex flex-col gap-4 pt-8">
<SheetContent side="right">
{/* SheetHeader/SheetTitle carry the panel's own padding and give
the underlying Radix dialog its accessible name; the
visually-hidden description satisfies aria-describedby. */}
<SheetHeader className="pb-0">
<SheetTitle>Menu</SheetTitle>
<SheetDescription className="sr-only">
Site navigation
</SheetDescription>
</SheetHeader>
{/* min-h-0 + overflow-y-auto so the list stays reachable on
short viewports instead of overflowing the panel. */}
<nav
aria-label="Mobile navigation"
className="flex flex-col gap-2"
className="flex flex-col gap-2 px-4 min-h-0 overflow-y-auto"
>
<NavLink
to="/projects"
className={navLinkClass}
onClick={() => setMobileOpen(false)}
>
<NavLink to="/projects" className={navLinkClass}>
Projects
</NavLink>
<NavLink
to="/help-wanted"
className={navLinkClass}
onClick={() => setMobileOpen(false)}
>
<NavLink to="/help-wanted" className={navLinkClass}>
Help Wanted
</NavLink>
<NavLink
to="/members"
className={navLinkClass}
onClick={() => setMobileOpen(false)}
>
<NavLink to="/members" className={navLinkClass}>
Members
</NavLink>
<NavLink
to="/volunteer"
className={navLinkClass}
onClick={() => setMobileOpen(false)}
>
Volunteer
</NavLink>
<Separator />
<p className="text-xs text-muted-foreground font-medium uppercase tracking-wide">
About
</p>
<NavLink
to="/pages/mission"
className={navLinkClass}
onClick={() => setMobileOpen(false)}
>
<NavLink to="/pages/mission" className={navLinkClass}>
Mission
</NavLink>
<NavLink
to="/pages/leadership"
className={navLinkClass}
onClick={() => setMobileOpen(false)}
>
<NavLink to="/pages/leadership" className={navLinkClass}>
Leadership
</NavLink>
<NavLink
to="/pages/code-of-conduct"
className={navLinkClass}
onClick={() => setMobileOpen(false)}
>
<NavLink to="/pages/code-of-conduct" className={navLinkClass}>
Code of Conduct
</NavLink>
<NavLink
to="/pages/hackathons"
className={navLinkClass}
onClick={() => setMobileOpen(false)}
>
<NavLink to="/pages/hackathons" className={navLinkClass}>
Hackathons
</NavLink>
<NavLink
to="/sponsor"
className={navLinkClass}
onClick={() => setMobileOpen(false)}
>
<NavLink to="/sponsor" className={navLinkClass}>
Sponsor
</NavLink>
<a
href="mailto:hello@codeforphilly.org"
className="text-sm font-medium text-muted-foreground hover:text-primary"
onClick={() => setMobileOpen(false)}
>
Contact
</a>
<Separator />
<a
href={GITHUB_URL}
target="_blank"
rel="noopener noreferrer"
className="text-sm font-medium text-muted-foreground hover:text-primary"
onClick={() => setMobileOpen(false)}
>
GitHub
</a>
<NavLink to="/volunteer" className={navLinkClass}>
Volunteer
</NavLink>
</nav>
<Separator />
<SearchBox inline />
<div className="px-4 pb-4">
<SearchBox inline />
</div>
</SheetContent>
</Sheet>
</div>
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/SearchBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export function SearchBox({ inline = false }: SearchBoxProps) {

return (
<div
className={`relative ${inline ? 'w-full' : 'w-48 focus-within:w-72 transition-all duration-200'}`}
className={`relative ${inline ? 'w-full' : 'w-24 lg:w-48 lg:focus-within:w-72 transition-all duration-200'}`}
>
<Input
ref={inputRef}
Expand All @@ -88,7 +88,7 @@ export function SearchBox({ inline = false }: SearchBoxProps) {
id="search-results-dropdown"
role="listbox"
aria-label="Search results"
className="absolute top-full left-0 right-0 mt-1 bg-popover border border-border rounded-md shadow-lg z-50 py-1 max-h-[28rem] overflow-y-auto"
className="absolute top-full right-0 min-w-72 mt-1 bg-popover border border-border rounded-md shadow-lg z-50 py-1 max-h-[28rem] overflow-y-auto"
>
{loading && results.length === 0 && (
<p className="px-3 py-2 text-sm text-muted-foreground">Searching…</p>
Expand Down
15 changes: 15 additions & 0 deletions apps/web/src/components/icons/GitHubIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** GitHub mark, sized to sit inline with text and icon buttons. */
export function GitHubIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="currentColor"
aria-hidden="true"
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
);
}
29 changes: 0 additions & 29 deletions apps/web/src/pages/HomeStub.tsx

This file was deleted.

16 changes: 1 addition & 15 deletions apps/web/src/pages/LoginPlaceholder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { GitHubIcon } from '@/components/icons/GitHubIcon';
import { useAuth } from '@/hooks/useAuth';
import { api, ApiError } from '@/lib/api';

Expand Down Expand Up @@ -46,21 +47,6 @@ const ERROR_MESSAGES: Record<ErrorCode, React.ReactNode> = {
),
};

function GitHubIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="currentColor"
aria-hidden="true"
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
);
}

function WhyGitHub() {
const [open, setOpen] = useState(false);

Expand Down
Loading