Files
Josh Wright b276cc9948
All checks were successful
Build and Deploy / Build & Push (push) Successful in 53s
polish: mobile view layout and usability improvements
Header:
- Hide "X of Y parks open this week" badge on mobile (hidden sm:inline-flex)
  — title + Coaster Mode button fit cleanly on a 390px screen

WeekNav:
- Arrow button padding 6px 14px → 10px 16px, minWidth: 44px for proper
  touch targets (Apple HIG recommends 44px minimum)
- Date label minWidth 200px → 140px, prevents crowding on small screens

ParkCard:
- Name container: flex: 1, minWidth: 0 so long park names don't push
  the status badge off-screen; name wraps naturally instead of overflowing
- Timezone abbreviation: opacity: 0.6 → color: var(--color-text-dim),
  semantic dimming instead of opacity for better accessibility
- Passholder label: 0.58rem → 0.65rem (was below WCAG minimum)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 09:11:34 -04:00

156 lines
4.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
interface WeekNavProps {
weekStart: string; // YYYY-MM-DD (Sunday)
weekDates: string[]; // 7 dates YYYY-MM-DD
isCurrentWeek: boolean;
}
const MONTHS = [
"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec",
];
function formatLabel(dates: string[]): string {
const s = new Date(dates[0] + "T00:00:00");
const e = new Date(dates[6] + "T00:00:00");
if (s.getFullYear() === e.getFullYear() && s.getMonth() === e.getMonth()) {
return `${MONTHS[s.getMonth()]} ${s.getDate()}${e.getDate()}, ${s.getFullYear()}`;
}
const startStr = `${MONTHS[s.getMonth()]} ${s.getDate()}`;
const endStr = `${MONTHS[e.getMonth()]} ${e.getDate()}, ${e.getFullYear()}`;
return `${startStr} ${endStr}`;
}
function shiftWeek(weekStart: string, delta: number): string {
const d = new Date(weekStart + "T00:00:00");
d.setDate(d.getDate() + delta * 7);
return d.toISOString().slice(0, 10);
}
export function WeekNav({ weekStart, weekDates, isCurrentWeek }: WeekNavProps) {
const router = useRouter();
const nav = (delta: number) => {
router.push(`/?week=${shiftWeek(weekStart, delta)}`);
};
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
if (e.key === "ArrowLeft") nav(-1);
if (e.key === "ArrowRight") nav(1);
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [weekStart]);
return (
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<button
onClick={() => nav(-1)}
aria-label="Previous week"
style={navBtnStyle}
onMouseOver={(e) => Object.assign((e.target as HTMLElement).style, navBtnHover)}
onMouseOut={(e) => Object.assign((e.target as HTMLElement).style, navBtnStyle)}
>
</button>
{!isCurrentWeek && (
<button
onClick={() => router.push("/")}
aria-label="Jump to current week"
style={todayBtnStyle}
onMouseOver={(e) => Object.assign((e.target as HTMLElement).style, todayBtnHover)}
onMouseOut={(e) => Object.assign((e.target as HTMLElement).style, todayBtnStyle)}
>
Today
</button>
)}
<span style={{
fontSize: "1rem",
fontWeight: 600,
color: "var(--color-text)",
minWidth: 140,
textAlign: "center",
letterSpacing: "-0.01em",
fontVariantNumeric: "tabular-nums",
}}>
{formatLabel(weekDates)}
</span>
<button
onClick={() => nav(1)}
aria-label="Next week"
style={navBtnStyle}
onMouseOver={(e) => Object.assign((e.target as HTMLElement).style, navBtnHover)}
onMouseOut={(e) => Object.assign((e.target as HTMLElement).style, navBtnStyle)}
>
</button>
</div>
);
}
const navBtnStyle: React.CSSProperties = {
padding: "10px 16px",
borderRadius: 8,
border: "1px solid var(--color-border)",
background: "var(--color-surface)",
color: "var(--color-text-muted)",
cursor: "pointer",
fontSize: "1rem",
lineHeight: 1,
transition: "background 150ms ease, border-color 150ms ease, color 150ms ease",
minWidth: 44,
textAlign: "center",
};
const navBtnHover: React.CSSProperties = {
padding: "10px 16px",
borderRadius: 8,
border: "1px solid var(--color-text-dim)",
background: "var(--color-surface-2)",
color: "var(--color-text-secondary)",
cursor: "pointer",
fontSize: "1rem",
lineHeight: 1,
transition: "background 150ms ease, border-color 150ms ease, color 150ms ease",
minWidth: 44,
textAlign: "center",
};
const todayBtnStyle: React.CSSProperties = {
padding: "5px 12px",
borderRadius: 6,
border: "1px solid var(--color-accent-muted)",
background: "transparent",
color: "var(--color-accent)",
cursor: "pointer",
fontSize: "0.75rem",
fontWeight: 600,
letterSpacing: "0.04em",
textTransform: "uppercase",
lineHeight: 1,
transition: "background 150ms ease, color 150ms ease",
};
const todayBtnHover: React.CSSProperties = {
padding: "5px 12px",
borderRadius: 6,
border: "1px solid var(--color-accent-muted)",
background: "var(--color-accent-muted)",
color: "var(--color-accent-text)",
cursor: "pointer",
fontSize: "0.75rem",
fontWeight: 600,
letterSpacing: "0.04em",
textTransform: "uppercase",
lineHeight: 1,
transition: "background 150ms ease, color 150ms ease",
};