style(web): wrap the calendar in one frame and dissolve the header into it - #956
Draft
NatnaelTaddese wants to merge 3 commits into
Draft
style(web): wrap the calendar in one frame and dissolve the header into it#956NatnaelTaddese wants to merge 3 commits into
NatnaelTaddese wants to merge 3 commits into
Conversation
NatnaelTaddese
marked this pull request as draft
August 27, 2026 15:21
…to it The header was the app's only carded header sitting on its only bare body: a rounded-2xl elevated card floating 6px above an edge-faded grid, the inverse of the sidebar's flat-header-over-card-stack grammar. The gap and the grid's `mx-px` border compensation patched that seam rather than fixing it. Move the border, rounding and shadow from the header onto the frame so one outline encloses the whole calendar, and lift only the toolbar band on `bg-background-elevated`, dissolving it downward through the column header into the page-colored grid. The fade is scoped to the column-header element rather than a percentage of the header, so it starts just under the toolbar in both views without a constant to keep in sync, and stretches with the week view's all-day band. Both grids now fade at the bottom edge only; a top ramp would wash in exactly where the header dissolves out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tion View transitions lift named elements into a flat overlay on the document root rather than nesting them under their DOM ancestors, so the frame's `overflow-hidden` and `rounded-2xl` stop clipping them mid-transition and their filled children paint as squares overhanging the rounded corners. The header used to carry both the rounding and the fill, so its snapshot was already round; moving the rounding onto the unnamed frame left the named children square. Round the two that reach a corner and have opaque content: the toolbar at the top, and the grid at the bottom, where the month view's last row of event pills is unaffected by the rule layer's edge fade. Both radii are the frame's 16px less its 1px border, which is exactly what `overflow-hidden` already clipped them to, so nothing changes statically. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The calendar hand-rolled every mask as an inline gradient string, wrote each one twice for the -webkit- prefix, and in the month grid set mask-composite by hand. Tailwind's edge-mask utilities shipped in 4.1 and 4.2.1 was already the pinned version when these constants were written, so this was avoidable. Each substitution emits the same CSS: mask-b-from-0%, mask-t-from-35%, and mask-b/x-from-[calc(100%-24px)] compose to the same gradients under the mask-composite: intersect the utilities set for themselves. Dropping the -webkit- pairs is safe because Vite's baseline target is Safari 16 and unprefixed mask-image landed in 15.4. The frame's inner radius stops being the magic number 15 and becomes calc(var(--radius-2xl) - 1px), so its stated derivation from the frame's own radius survives a change to rounded-2xl instead of going quietly stale. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
NatnaelTaddese
force-pushed
the
feature/calendar-unified-frame
branch
from
September 1, 2026 01:58
6646d3b to
c1dcb14
Compare
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.
Stacked on #938 — please merge that first.
Why
On
/dashboardthe calendar pane reads as two unrelated components stacked on each other, and the cause is a grammar inversion between the two panes:SyncStatus,EventGraphsit directly onbg-background, no border) above agap-1.5stack ofrounded-2xlelevated cards.rounded-2xlelevated card header floating 6px above a bare, edge-faded grid.So the header was the only carded header in the app, sitting on the only bare body. The
gap-1.5and the grid'smx-pxborder compensation were patches over that seam rather than fixes for it.What changed
One border now wraps the entire calendar, and inside it the toolbar band is lifted on
bg-background-elevatedand dissolves downward into the page-colored grid. The outline stops belonging to the header and starts belonging to the calendar.calendar-frame.tsx— border/rounding/shadow move from the<header>to the outer container, which stays transparent so its interior inheritsbg-backgroundand matches the sticky hour gutter. The toolbar row takesbg-background-elevated; the column-header row gets anaria-hiddenmasked fill layer behind it.week-grid.tsx/month-grid.tsx— the grids' vertical edge ramp becomes bottom-only.Two decisions worth flagging for review:
The fade is scoped to the column-header element, not to a percentage of the header. The week strip's column header is
HEADER_HEIGHT + bandHeight(it grows with the all-day band added in #938); the month grid's is unsized, ~30px. A percentage stop would land mid-toolbar in one view and below it in the other. Scoping it to the column-header row makes "dissolve from just under the toolbar" true in both, with no constant to keep in sync, and it stretches with the all-day band on its own. All-day pills render in therelativelayer above the fill, so they stay fully opaque.Both grids fade at the bottom only. Left as they were, the grid would wash in over its top 24px exactly where the header dissolves out, giving a pale dead band at the merge point. The day header's column-rule fade is deliberately untouched — its upward ramp runs opposite to the new surface fade over the same band, so the column rules strengthen as the fill evaporates. That counter-motion is the effect.
mx-pxis gone: it existed only to compensate for the header card's own 1px border, and header and grid now share one content box inside a single border.Verified
bun run typesandbun run lintpass. Checked live in both themes and both views, signed in with events loaded.mx-pxdid not shift them. The rules strengthen as the fill evaporates, which is the intended counter-motion.background: transparentwith a1pxborder at16pxradius, the toolbar on the elevated fill at a15pxtop radius, and the fill layer masked withlinear-gradient(rgb(0,0,0) 0%, rgba(0,0,0,0) 100%)undermask-composite: intersect.Follow-up: these styles are now Tailwind, not inline
Review asked why any of this was written as inline
style={{}}when the app is Tailwind-first (4 arbitrary-property usages in the whole codebase, and nomask-*utilities anywhere). There was no reason:tailwindcss@4.2.1was already the pinned version when the first mask constant was written, and its edge-mask utilities shipped in 4.1.Every mask, the inner radius and the four
view-transition-names now use utilities. Each substitution was checked against the compiled CSS and againstgetComputedStyleon the live components:linear-gradient(to bottom, black, transparent)mask-b-from-0%linear-gradient(to bottom, black calc(100% - 24px), transparent)mask-b-from-[calc(100%-24px)]linear-gradient(to top, black 35%, transparent)mask-t-from-35%mask-x-from-[calc(100%-24px)]borderTop/BottomRadius: 15rounded-t-/rounded-b-[calc(var(--radius-2xl)-1px)]viewTransitionName: "calendar-header"[view-transition-name:calendar-header]Two of these were worse as inline styles rather than merely different:
INNER_RADIUS = 15was a magic number whose documented derivation from the frame's own radius was not enforced — changerounded-2xland it goes quietly stale.calc(var(--radius-2xl) - 1px)keeps the derivation live, and still computes to15px.-webkit-, and the month grid also hand-setmaskComposite: "intersect"alongside the legacyWebkitMaskComposite: "source-in". Tailwind emitsmask-composite: intersectfor free; there is no browserslist here and Vite's baseline target is Safari 16, while unprefixedmask-imagelanded in 15.4. Zero-webkit-mask properties remain on the rendered tree.maxHeight, the week grid'sGUTTER_WIDTH/HEADER_HEIGHTgeometry and the month grid'sCELL_RULESstay inline — the first is a runtime prop and the rest are read by JS for scroll and layout math.Net −39 lines.
bun run types,bun run lintand all 468 tests pass. Verified visually against a session-free harness rendering the realWeekGridandMonthGrid: the frame, the header dissolve, the bottom-only fades, the month grid's horizontal ramp and the week↔month transition all render as before, with the corners staying rounded mid-transition.🤖 Generated with Claude Code