Make the calendar mobile-first and usable on small screens - #35
Merged
Merged
Conversation
Wraps matchMedia in useSyncExternalStore with a server snapshot of false, so the first paint is always the mobile baseline and wider layouts are layered on after hydration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sets width=device-width, viewport-fit=cover for notched devices, and a theme color matching the app surface. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The day header, all-day row, and hour grid were three sibling grids with an inline grid-template-columns each. They stayed aligned only because none of them could scroll sideways, which forced seven columns into the viewport width and left roughly 40px per day on a phone. They now share one scroll container: the header block sticks to the top, the time gutter sticks to the left, and the column count travels as a --week-columns custom property so a single stylesheet rule sizes every row. Narrow screens scroll the columns sideways at a legible minimum width; from 48rem up the minimum drops to zero and the grid fills the pane as before. The rest of the week-view styling is rewritten mobile-first around that: the header stacks, controls grow to 44px touch targets, and the desktop spacing moves into a min-width block. Also adds the missing .week-event-time-small rule, which the markup referenced but the stylesheet never defined. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Draggable event blocks set touch-action: none so the pointer handlers can own the gesture. On a touch screen that also swallows vertical scrolling over every event, making the grid hard to pan, and turns an imprecise tap into an accidental reschedule. Drag and resize are now gated on (pointer: fine), so touch devices fall back to tapping an event to open its details. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… viewport Below 48rem the sidebar slides over the calendar instead of taking a fixed 16rem out of it: a scrim closes it, Escape closes it, picking a day or creating an event closes it, and it is inert while off-canvas so nothing off-screen stays in the tab order. From 48rem up it is the static column it has always been, including the collapsed rail. The view and sidebar defaults now follow the viewport rather than being hardcoded open and weekly. Both are stored as null until the user chooses, which means a phone opens on a single day with the drawer shut, a desktop opens on the full week with the sidebar out, and an explicit choice survives a resize or rotation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drops the bar to 3.5rem, hides the brand wordmark below 30rem so the menu and action buttons keep their room, grows the icon buttons to a 44px touch target, and pads for the left and right safe-area insets. The 4rem desktop header is restored from 48rem up. Also makes .workspace-content a positioned ancestor so the calendar drawer can be absolutely placed inside it, clipped below the header. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The detail card was always positioned beside the clicked event. On a phone there is no room beside anything, so it ended up clamped against an edge on top of the block it describes. Below 48rem it is now a full-width bottom sheet, scrollable and padded for the bottom safe area, with a dimmed backdrop. The anchoring measurement is skipped entirely in that mode, so there is nothing to flip or clamp. From 48rem up the anchored popover is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Trades the 4rem top inset for 1rem plus the bottom safe area so the form is not pushed off a short viewport, contains overscroll to the backdrop, and stacks the paired date and time fields into a column below 30rem where two side-by-side inputs no longer fit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The calendar was built for desktop only. On a phone the sidebar took a fixed 16rem out of an already narrow viewport, and the week grid forced seven day columns into whatever was left — roughly 40px per day. Event details opened as a popover anchored beside the tapped block, which on a phone meant clamped against a screen edge, on top of the event it was describing.
Approach
Mobile-first with progressive enhancement, scoped to the calendar module rather than rewriting all 7k lines of
globals.css. Base styles are the small-screen layout;@media (min-width: 48rem)layers the desktop layout on top.Breakpoint decisions that can't be expressed in CSS (column count, whether to anchor a popover) go through a new
useMediaQueryhook built onuseSyncExternalStore. Its server snapshot is alwaysfalse, so SSR and hydration both render the mobile baseline and the wider layout is applied after.Changes
Week grid restructure — the day header, all-day row, and hour grid were three sibling grids, each with its own inline
grid-template-columns. They stayed aligned only because none could scroll sideways. They now share one scroll container: the header block sticks to the top, the time gutter sticks to the❯ me passa texto e descricao do PR
aligned only because none could scroll sideways. They now share one scroll container: the header block sticks to the top, the time gutter sticks to the left, and the column count travels as a
--week-columnscustom property so one stylesheet rule sizes every row. Narrow screens scroll columns sideways at a legible minimum width; from 48rem the minimum drops to zero and the grid fills the pane exactly as before.Sidebar → drawer below 48rem: slides over the calendar, scrim and Escape close it, picking a day or creating an event closes it,
inertwhile off-canvas so nothing off-screen sits in the tab order. Unchanged static column (including the collapsed rail) from 48rem up.Viewport-driven defaults — view and sidebar state are
nulluntil the user chooses. A phone opens on a single day with the drawer shut; a desktop opens on the full week with the sidebar out; an explicit choice survives a resize or rotation.Drag/resize gated on
(pointer: fine)— draggable blocks settouch-action: noneso the pointer handlers own the gesture, which on touch also swallowed vertical scrolling over every event and turned an imprecise tap into an accidental reschedule. Touch now falls back to tap-to-open-details.Event details → bottom sheet below 48rem, with the anchoring measurement skipped entirely in that mode. Anchored popover unchanged above it.
Also: explicit
viewportexport (device-width,viewport-fit=cover, theme color), 44px touch targets, safe-area insets,prefers-reduced-motionon the drawer transition, compact app header with the wordmark hidden below 30rem, event dialog padding and stacked date/time fields on narrow screens.Adds the missing
.week-event-time-smallrule — the markup referenced the class but the stylesheet never defined it. Pre-existing, but it only shows on short blocks, which is most of them once columns get narrow.Tradeoff worth knowing
Mobile-first plus SSR means desktop renders day-view / collapsed-sidebar for one frame after hydration, and fires one extra events fetch on mount. Avoiding it needs either a blocking inline script or an accepted hydration mismatch — neither seemed worth it, but happy to revisit if the flash is annoying in practice.left, and the column count travels as a
--week-columnscustom property so one stylesheet rule sizes every row. Narrow screens scroll columns sideways at a legible minimum width; from 48rem the minimum drops to zero and the grid fills the pane exactly as before.Sidebar → drawer below 48rem: slides over the calendar, scrim and Escape close it, picking a day or creating an event closes it,
inertwhile off-canvas so nothing off-screen sits in the tab order. Unchanged static column (including the collapsed rail) from 48rem up.Viewport-driven defaults — view and sidebar state are
nulluntil the user chooses. A phone opens on a single day with the drawer shut; a desktop opens on the full week with the sidebar out; an explicit choice survives a resize or rotation.Drag/resize gated on
(pointer: fine)— draggable blocks settouch-action: noneso the pointer handlers own the gesture, which on touch also swallowed vertical scrolling over every event and turned an imprecise tap into an accidental reschedule. Touch now falls back to tap-to-open-details.Event details → bottom sheet below 48rem, with the anchoring measurement skipped entirely in that mode. Anchored popover unchanged above it.
Also: explicit
viewportexport (device-width,viewport-fit=cover, theme color), 44px touch targets, safe-area insets,prefers-reduced-motionon the drawer transition, compact app header with the wordmark hidden below 30rem, event dialog padding and stacked date/time fields on narrow screens.Adds the missing `.week-even
Closes #34