fix: responsive park month calendar — dot-only on mobile, full pill on desktop
All checks were successful
Build and Deploy / Build & Push (push) Successful in 53s

Mobile cells (~55px wide) can't fit hours text legibly, so show only a
colored dot when open and a dash when no data. Desktop restores the full
pill (hours + tz abbreviation) via Tailwind responsive classes. Row height
is controlled by a new .park-calendar-grid CSS class: 72px fixed on mobile,
minmax(96px, auto) on sm+, keeping desktop cells from looking cramped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Josh Wright
2026-04-05 09:31:48 -04:00
parent a31dda4e9e
commit 040c1e4d70
2 changed files with 52 additions and 24 deletions

View File

@@ -104,6 +104,18 @@
background: var(--color-surface-hover) !important;
}
/* ── Park month calendar — responsive row heights ───────────────────────── */
/* Mobile: fixed uniform rows so narrow columns don't cause height variance */
.park-calendar-grid {
grid-auto-rows: 72px;
}
/* sm+: let rows breathe and grow with their content (cells are wide enough) */
@media (min-width: 640px) {
.park-calendar-grid {
grid-auto-rows: minmax(96px, auto);
}
}
/* ── Pulse animation for skeleton ───────────────────────────────────────── */
@keyframes pulse {
0%, 100% { opacity: 1; }

View File

@@ -119,15 +119,15 @@ export function ParkMonthCalendar({ parkId, year, month, monthData, today, timez
</div>
{/*
All day cells in ONE flat grid so every row shares the same
fixed height — no per-week wrapper divs that could produce
rows of different heights on mobile.
All day cells in ONE flat grid — eliminates per-week wrapper
divs that caused independent row heights and the slant effect.
Row height is controlled responsively via .park-calendar-grid CSS:
mobile = 72px fixed, sm+ = minmax(96px, auto).
*/}
<div style={{
display: "grid",
gridTemplateColumns: "repeat(7, 1fr)",
gridAutoRows: 76,
}}>
<div
className="park-calendar-grid"
style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)" }}
>
{cells.map((cell, idx) => {
const ci = idx % 7;
const isLastRow = idx >= cells.length - 7;
@@ -137,6 +137,7 @@ export function ParkMonthCalendar({ parkId, year, month, monthData, today, timez
if (!cell.day || !cell.iso) {
return (
<div key={idx} style={{
overflow: "hidden",
background: ci === 0 || ci === 6 ? "var(--color-weekend-header)" : "transparent",
borderRight,
borderBottom,
@@ -183,38 +184,53 @@ export function ParkMonthCalendar({ parkId, year, month, monthData, today, timez
{cell.day}
</span>
{/* Status — single-line pill so all cells stay uniform height */}
{/* ── Mobile status: colored dot only (sm:hidden) ──────── */}
{/* Cells are ~55px wide on mobile — no room for hours text */}
{!dayData ? (
<span style={{ fontSize: "0.75rem", color: "var(--color-text-dim)" }}></span>
<span className="sm:hidden" style={{ fontSize: "0.7rem", color: "var(--color-text-dim)" }}></span>
) : isOpen ? (
<div className="sm:hidden" style={{
width: 7, height: 7, borderRadius: "50%", flexShrink: 0,
background: isPH ? "var(--color-ph-border)" : "var(--color-open-border)",
}} />
) : null}
{/* ── Desktop status: full pill with hours (hidden sm:block) */}
{!dayData ? (
<span className="hidden sm:inline" style={{ fontSize: "0.75rem", color: "var(--color-text-dim)" }}></span>
) : isPH && isOpen ? (
<div style={{
<div className="hidden sm:block" style={{
background: "var(--color-ph-bg)",
border: "1px solid var(--color-ph-border)",
borderRadius: 4,
padding: "2px 4px",
overflow: "hidden",
borderRadius: 5,
padding: "3px 6px",
}}>
<div style={{ fontSize: "0.58rem", fontWeight: 700, color: "var(--color-ph-label)", textTransform: "uppercase", letterSpacing: "0.04em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
<div style={{ fontSize: "0.6rem", fontWeight: 700, color: "var(--color-ph-label)", textTransform: "uppercase", letterSpacing: "0.05em" }}>
Passholder
</div>
<div style={{ fontSize: "0.6rem", color: "var(--color-ph-hours)", marginTop: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
{dayData.hoursLabel} <span style={{ opacity: 0.6 }}>{tzAbbr}</span>
<div style={{ fontSize: "0.65rem", color: "var(--color-ph-hours)", marginTop: 2 }}>
{dayData.hoursLabel}
</div>
<div style={{ fontSize: "0.58rem", color: "var(--color-ph-label)", opacity: 0.75, marginTop: 1, letterSpacing: "0.04em" }}>
{tzAbbr}
</div>
</div>
) : isOpen ? (
<div style={{
<div className="hidden sm:block" style={{
background: "var(--color-open-bg)",
border: "1px solid var(--color-open-border)",
borderRadius: 4,
padding: "2px 4px",
overflow: "hidden",
borderRadius: 5,
padding: "3px 6px",
}}>
<div style={{ fontSize: "0.6rem", color: "var(--color-open-hours)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
{dayData.hoursLabel} <span style={{ opacity: 0.6 }}>{tzAbbr}</span>
<div style={{ fontSize: "0.65rem", color: "var(--color-open-hours)" }}>
{dayData.hoursLabel}
</div>
<div style={{ fontSize: "0.58rem", color: "var(--color-open-hours)", opacity: 0.6, marginTop: 1, letterSpacing: "0.04em" }}>
{tzAbbr}
</div>
</div>
) : (
<span style={{ fontSize: "1rem", color: "var(--color-text-dim)", lineHeight: 1 }}>·</span>
<span className="hidden sm:inline" style={{ fontSize: "1rem", color: "var(--color-text-dim)", lineHeight: 1 }}>·</span>
)}
</div>
);