# Junior Developer — Calendar View Fix
**Owner:** Junior developer
**Status:** Ready to implement
**Scope:** The **Kalender** view only (`/calendar`). Do not touch Dashboard, Settings, Tips, or backend.
**Goal:** Make the month calendar render as a real 7-column grid that looks like a calendar, behaves correctly for Swedish users, and is keyboard/mobile friendly — **without** adding a calendar library and **without** rewriting the working planning logic.
---
## TL;DR — what's actually wrong
The calendar **logic is already correct and complete**. `Calendar.tsx` builds weeks, pads leading/trailing days, maps planned days to colored events, and opens the day-edit modal with full CRUD. The markup already uses good BEM class names (`calendar__grid`, `calendar-day`, `calendar-day__number`, `calendar__weekday`, …).
**The only real problem: those classes have no CSS.** The app ships exactly one stylesheet (`header.css`) and the calendar's classes are unstyled, so every cell falls back to a default block `
` and stacks vertically. That vertical list of "31 1 2 3 …" in the screenshot is unstyled markup, nothing more.
So this is a **CSS task first**, with a few small correctness fixes layered on top. **We are building this ourselves — no FullCalendar / react-big-calendar.** A month grid is `display: grid` + cell styling; a library would force our bespoke day-type / dubbeldag / age-4 model into its generic event API and is not worth it.
---
## Guardrails (read before starting)
- **Do NOT add a dependency.** No FullCalendar, react-big-calendar, day-picker, Tailwind, etc. Plain CSS only.
- **Do NOT rewrite the planning logic.** The week-building loop, `getEventsForDate`, the modal wiring, and the CRUD handlers in `Calendar.tsx` work — leave them unless a task here explicitly says to change them.
- **TypeScript only** — no `.js`/`.jsx`. No `as any`, `@ts-ignore`, or `@ts-expect-error` (CI fails on ESLint warnings with `--max-warnings 0`).
- **Match the existing CSS pattern:** components own a co-located stylesheet imported at the top of the file — e.g. `Header.tsx` imports `./header.css`, `PlannedDayModal.tsx` imports `./planned-day-modal.css`. Follow that exact pattern for the calendar. **Do not** wait on a global stylesheet to land.
- **Read before edit.** Open every file before changing it; confirm structure first.
- Keep `npx tsc --noEmit`, `npm run lint`, and `npm run coverage` green after every task.
---
## Files you will touch
- `frontend/src/pages/Calendar.tsx` — add a CSS import; small correctness edits (P2).
- `frontend/src/pages/calendar.css` — **new** stylesheet (the bulk of the work).
- `frontend/src/components/common/CalendarDay.tsx` — only if P2 needs a "today"/weekend hook.
- Tests: `frontend/src/components/common/CalendarDay.test.tsx`, and a `Calendar.test.tsx` if one doesn't exist.
> ⚠️ **Confirm git state first.** `PlannedDayModal.tsx`, `ManualFetchModal.tsx`, and their CSS appear to be **untracked** (created during the MVP work but not committed). Run `git status` before you start. Don't accidentally revert or clobber them — commit or stash deliberately.
---
## P1 — Make it an actual calendar grid (the core fix)
**Problem:** `calendar__grid` has no layout, so the seven weekday headers and ~35 day cells render as a vertical stack.
**Do:**
1. Create `frontend/src/pages/calendar.css`.
2. At the top of `Calendar.tsx`, add `import './calendar.css'` (mirror how `Header.tsx` imports `./header.css`).
3. Style the structure that already exists in the JSX:
- `.calendar` — center it, constrain width (~`max-width: 920px; margin: 0 auto; padding: 16px`).
- `.calendar__header` — `display: flex; align-items: center; justify-content: space-between;` so the `←` / month title / `→` sit on one row. Style the nav buttons as clear, tappable controls (min 36×36px).
- `.calendar__grid` — **this is the key rule:** `display: grid; grid-template-columns: repeat(7, 1fr);` with a small `gap`. The weekday headers and day cells are direct children, so they'll flow into the 7 columns automatically.
- `.calendar__weekday` — centered, muted, smaller, semibold column header.
- `.calendar-day` — give it a min-height (~`84px`), light border/`border-radius`, white background, padding, `cursor: pointer`, and a hover state. Day number top-left (`.calendar-day__number`).
- `.calendar-day--disabled` — the leading/trailing days from adjacent months: muted text, faded background, no hover affordance.
- `.calendar-day--has-events` — subtle emphasis is fine (optional).
- `.calendar-day__events` — a small row/wrap of event chips at the bottom of the cell.
- `.calendar-day__event` — the colored dot/bar per planned day. Keep it small (a dot or a thin bar), **not** a full-width block. Colors come from the inline `backgroundColor` style already set in `CalendarDay.tsx` — don't hardcode them again in CSS.
- `:focus-visible` outline on `.calendar-day` (it's `role="button" tabIndex={0}` and already handles Enter/Space).
4. **Fix the legend.** Right now the legend reuses `CalendarEvent`, which renders full-width colored bars (the green/orange/blue stripes in the screenshot). Style `.calendar__legend` as a compact horizontal row of small swatch + label pairs. Either restyle `.calendar-event` when it sits inside `.calendar__legend-item` (small inline-flex, fixed-size swatch, auto width) **or** replace the legend markup with simple swatch spans. Keep it tidy and small.
**Acceptance:** The Kalender view shows a recognizable month grid — 7 columns, weekday headers across the top, square-ish day cells, adjacent-month days dimmed, planned days marked with small colored dots, and a compact legend. No vertical stacking. Looks calm and readable on desktop.
---
## P2 — Correctness & polish (Swedish conventions)
Do these after P1 looks right. They're small but matter for a Swedish app.
1. **Monday-first week.** Sweden starts the week on Monday, not Sunday.
- Weekday headers are currently `['Sun','Mon',…,'Sat']` in `Calendar.tsx`. Change to Swedish, Monday-first: `['Mån','Tis','Ons','Tor','Fre','Lör','Sön']`.
- The leading-blank calculation uses `firstDay.getDay()` (0 = Sunday). Convert to Monday-based: `const startOffset = (firstDay.getDay() + 6) % 7;` and use `startOffset` where `startingDayOfWeek` is used (both the leading-pad loop and any references). Re-verify the trailing-pad math still fills to a multiple of 7.
- **Test the boundary:** a month that starts on Sunday (e.g. **March 2026** starts Sunday) must show that Sunday in the **last** column, not the first.
2. **Highlight "today."** Add a `calendar-day--today` modifier when a cell's date equals the real current date (compare `YYYY-MM-DD` strings; reuse the existing `formatDate` helper — don't introduce a timezone bug by using `Date` comparison). Give it a distinct but subtle ring/background. (You can compute this in `Calendar.tsx` and pass a prop into `CalendarDay`, or compute inside `CalendarDay`.)
3. **Weekend tint (optional, nice-to-have).** Lightly tint Saturday/Sunday columns so the week structure reads at a glance.
4. **Default month.** `Calendar.tsx` hardcodes `useState(new Date(2026, 5))` (June 2026). Default to the **current month** (`new Date()`) instead, so the calendar opens on a useful month. Keep the existing prev/next navigation.
> **Out of scope but worth flagging to the senior, don't fix here:** the header shows a stray "August" dropdown **and** an "August" link next to the parent name (visible top-right in the screenshot) — that looks like a duplicate/placeholder child selector in `Header.tsx`, unrelated to the calendar. Note it; leave it.
**Acceptance:** Week runs Mån→Sön; a Sunday-starting month renders correctly; today is visibly marked; the view opens on the current month.
---
## P3 — Tests
Keep the frontend coverage gate (90%) green.
- **`CalendarDay`:** assert the grid cell renders the day number, applies `--disabled` when `isCurrentMonth={false}`, renders one `.calendar-day__event` per event with the right `backgroundColor`, fires `onClick` on click and on Enter/Space, and applies `--today` when given today's date.
- **`Calendar` (page):** with a mocked `usePlannedDays`/`useWorkspace`, assert that 7 weekday headers render, that the grid contains the expected number of day cells for a known month, that the **Monday-first** ordering is correct (first header is "Mån"), and that clicking a current-month day opens the modal. Use the boundary month from P2 as a regression test.
- Don't delete or comment out existing tests to go green — fix the code or update the test with a clear reason.
**Acceptance:** `npx tsc --noEmit`, `npm run lint` (zero warnings), and `npm run coverage` (≥90%) all pass.
---
## Definition of done
- Kalender view renders as a proper 7-column month grid, styled calmly and legibly, with a compact legend and small event markers.
- Week is Monday-first with Swedish weekday labels; Sunday-starting months render correctly; today is highlighted; opens on the current month.
- No new dependencies; no `.js`/`.jsx`; no type suppressions.
- All three frontend gates pass; existing modal/Dashboard behavior untouched.
- `graphify update .` run after the code changes (per repo protocol).
---
## Suggested commit slices
1. `feat(calendar): add calendar.css grid layout + styled day cells and legend` (P1)
2. `fix(calendar): Monday-first Swedish week, today highlight, default to current month` (P2)
3. `test(calendar): grid layout, week ordering, and day-cell coverage` (P3)
---
## A note on "build vs. library" (for context)
We deliberately chose to build this ourselves. The hard parts of a calendar — date math, navigation, event mapping, the day-edit flow — are already done and working. The gap was purely CSS. A third-party calendar library would mean adapting our domain model (sjukpenning / lägstanivå / vacation day types, dubbeldagar, the age-4 cutoff, reserved days) to its generic event API and restyling it anyway — more work and less control, not less. A fixed month grid is the cheapest possible calendar to own. If we ever need drag-to-reschedule across an agenda view, revisit then; not now.