Planning
MonthCalendar
The month at a glance, and the place to plan.
Preview
Props
| Prop | Type | Description |
|---|---|---|
strip | boolean | Phones (with compact and scroll: 'weeks'): the grid shows one week, swiped sideways (or the arrows) a week at a time; tapping the month title opens the scrolling month view, and picking a day folds it back. |
agendaView | 'day' | 'week' | week: the day list always shows the chosen day's week, with no Day and Week switch (phones). |
onItemClick | (item: CalendarItem) => void | Clicking (or Enter on) an item's chip in the grid calls this too, after picking its day (Makado opens the project's drawer). |
onItemHold | (item: CalendarItem, anchor: HTMLElement) => void | A day list card held and let go without moving (touch), right clicked, or opened with the context menu key: the item's options, anchored to its card. Holding and moving still drags it to a day. |
fill | boolean | Fills its container's height: the grid takes the room left and scrolls inside (with scroll: 'weeks'), and the day list beside it is the same height and scrolls on its own. The container needs a height. |
scroll | 'month' | 'weeks' | weeks: one continuous run of weeks that scrolls up and down without end, opening with this week on top; the title follows the month in view. Default month shows one month at a time. The frame's height comes from --gv-cal-height. |
agendaWeek | boolean | Lets the day list switch between the chosen day and its whole week (Day and Week). |
pinned | boolean | The month stays in place while the page scrolls, so the day list moves under it (the Calendar page on phones). Set --gv-cal-pin-top to clear anything fixed above it, --gv-cal-pin-surface to the surface behind it, and --gv-cal-pin-bleed to reach the page edges. |
mode | 'projects' | 'todos' | projects shows projects on their working date with stage marks and project cards; todos (default) shows to-dos with checks. |
itemsRequired | CalendarItem[] | |
label | string | |
month | string | Month to open on, "2026-10". Defaults to the selected day's month. |
today | string | ISO day treated as today. Defaults to the device's date. |
selected | string | |
defaultSelected | string | |
onSelect | (date: string) => void | |
weekStart | 0 | 1 | 2 | 3 | 4 | 5 | 6 | The first day of the week, 0 (Sunday, default) to 6 (Saturday). |
days | number[] | Which weekdays to show, 0 (Sunday) to 6 (Saturday); all seven by default. [1, 2, 3, 4, 5] shows a working week. |
workDays | number[] | Your workdays, 0 (Sunday) to 6 (Saturday): the other shown days are shaded a little darker (background-rest). |
maxPerDay | number | No longer used: a day shows as many chips as fit its height, then "2 more". |
compact | boolean | Phone layout: dates with dots, the day list below. |
agenda | boolean | Set false to hide the day list. |
agendaWidth | number | Width of the day list in px (default 300). |
headingLevel | number | Heading level of the month title (default 2); the day list title is one level below. |
legend | boolean | |
emptyText | string | |
addLabel | string | |
addPlaceholder | string | |
allowAdd | boolean | |
readOnly | boolean | |
roundCheck | boolean | |
onChange | (items: CalendarItem[]) => void | Called with every item after each move, check or add. |
dayNotes | DayNote[] | Holidays, time off and custom notes, shown in the grid and the day list. |
onDayNotesChange | (notes: DayNote[]) => void | Turns on adding, editing and removing day notes from the day list; called with every note after each change. |
onDayDoubleClick | (date: string, anchor: HTMLElement) => void | Double-clicking a day (not a chip on it) calls this with its ISO date, for example to start a new project there. Double-clicking a day (not a chip) calls this with its ISO date and the day's cell, to anchor a Popover such as a quick add. The day's date button takes focus first. |
onAdd | (date: string, anchor: HTMLElement) => void | Makes the day list's add button call this (with the selected day and its cell) instead of opening the inline field: the keyboard and phone path to the same quick add. |
deadlines | CalendarDeadline[] | Publish dates and draft due dates, shown in the grid and the day list. They cannot be dragged. |
onDeadlineMove | (deadline: CalendarDeadline, date: string) => void | Lets deadlines move: drag one to another day, or focus it and press Alt with an arrow key. Called with the deadline and its new day. |
onReorder | (id: string, targetId: string, after: boolean) => void | Put an item before or after another (its priority): drop it on a chip or a card, or press Alt Shift with up or down. Dropped on an item on another day, it takes that day too. |
headActions | React.ReactNode | Extra controls in the title row, before the month arrows (a search). |
agendaTitle | string | A title over the day list, in the same type as the month ("Agenda"), level with the month's title row. |
onAgendaToggle | () => void | A button to hide or show the day list, always in the top right corner. Pair with agenda={false} while hidden. |
className | string |
Guidelines
The month at a glance, and the place to plan. On Home it shows projects on their working date (mode: 'projects'), the way a Notion calendar does: each day lists project titles as chips with a stage mark, and you drag a chip to another day to move its working date. Pick a day and its projects open beside the grid as project cards. In the default todos mode it does the same for to-dos, with a check on each. On phones the grid shrinks to dates with dots and the list sits below.
Provide: mode (projects or todos, the default) and items. A project is { id, date, title, brand, brandTone, status, formats, amount, href, late }, with status from Picker.projectStatuses. A to-do is { id, date, title, brand, time, status, done, href }, with status from MonthCalendar.statuses (todo, in-progress, review, scheduled, overdue, done) or { variant, label }. date is an ISO day ("2026-10-19"). Optional month (the month to open on), today (defaults to the device's date), defaultSelected or selected with onSelect(date), weekStart (0 for Sunday, 1 for Monday), a day shows as many chips as fit its height, then "2 more" (maxPerDay is no longer used), compact (the phone layout), agenda (false hides the day list), agendaWidth (default 300), headingLevel (the month title's level, default 2), legend, addLabel, addPlaceholder, allowAdd, readOnly, emptyText, and onChange(items) to save changes.
- A project's mark shows its stage as a filling pie: an empty ring for To do, then a fifth more for each stage (Pre-production, Filming, Editing, In review), and a green check for Published. The legend under the grid is one short row: To do, each stage's mark in order, Published (each mark names its stage on hover), then Late.
- A project flagged
lategets a red chip and "3 days late" on its card; moving it to a new day clears the flag. Projects are never marked late just for having a past date, since a working date is a plan, not a deadline. - In
todosmode every to-do shows one of six statuses with a shaped mark (ring, half, dot in a ring, solid, alert, check), and anything still open on a past day shows Overdue and is gathered under "From earlier" on today's list. - While dragging, the day under the pointer shows a dashed preview of where the chip will land.
- Weeks. With
scroll: 'weeks'the grid is one run of weeks instead of one month: it opens with this week on top and scrolls up into the past and down into the future without end (8 more weeks load as you near either end, and your place holds when they go on top). The weekday row stays at the top of the frame, the title names the month of the top week in view, and days of other months take the out-of-month shade, so the month in view stands out. The arrows jump a month and Today brings this week back to the top. There is no sideways swipe in this mode. Set the frame's height with--gv-cal-height. The Calendar page uses it. - Fill. With
fill(not on phones) the calendar takes its container's full height: the grid gets whatever room is left after the title and legend and scrolls inside, and the day list beside it is always the same height as the calendar and scrolls on its own. Give the container a height (the Calendar page sizes it to the window, so the page itself never scrolls). - Day or Week. With
agendaWeekthe day list gets a Day and Week switch. Week shows the chosen day's week, day by day ("Today, Tuesday, Oct 20" for today), each with its deadlines and items; days with nothing on them are left out, and the header reads "Week of Oct 18" with a "This week" tag when it holds today. - Pinned. With
pinnedthe month (title, arrows and grid) stays in place while the page scrolls, and the day list moves under it on a solid surface with a hairline below the grid. The Calendar page uses it on phones, so the month sits on top and the chosen day's projects scroll below. Set--gv-cal-pin-topto the height of anything fixed above it (the phone top bar); that band is filled with the surface so nothing scrolls into view behind it. Set--gv-cal-pin-surfaceto the surface the calendar sits on (defaultbackground-elevated;background-pageon phones) and--gv-cal-pin-bleedto the page gutter so the surface reaches the edges. Compact days get a little shorter so the list has room. - When a day has more than fits, "2 more" opens that day in place to show everything, and "Show less" folds it back. It also picks the day. Changing the month folds it.
- Change the month with the arrows, Today, a sideways swipe on the grid (touch or trackpad), or Page Up and Page Down. Each change is announced. A swipe that starts on a chip is left for dragging.
- Today's date sits in a blue circle; the chosen day gets a blue outline. Days from the next or previous month sit on
background-layer-1. - In
projectsmode a chip leads with the stage mark and title; the brand sits smaller underneath (11px,subdued), lined up with the title. No brand tile on the calendar: the title is what people scan for. The day list's project cards show the tile andbrandLogo. Intodosmode a chip shows the title only. Times, amounts and the check live in the day list. - To reschedule, drag a chip or a card onto another day (or onto the open day list), or focus it and press Alt with an arrow key: left and right move a day, up and down move a week. Every move and check is announced.
- On Home the calendar and the Projects board share one list of projects:
onChange(items)from the calendar updates dates, the board'sonChange(columns)updates stages, and both redraw from that list. - Day notes. Pass
dayNotes({ id, kind, label, date, endDate }, withkindfromMonthCalendar.dayNoteKinds:holiday,time-off,custom) to annotate days or runs of days. A noted day shows a small pill label beside the date: the label in 10px bold white onlabel-fillgray; custom notes add their chosen icon in white, holidays and time off show the label alone (every kind's icon shows in the day list:flagfor holidays,clockfor time off,sticky-noteor a preset for custom notes), truncated when the day is narrow (the full label is in the day list). A custom note can carry one of the preset icons inMonthCalendar.noteIcons(note, rocket, calendar, star, heart, smile, lightbulb, camera, microphone, music note, location, bell), picked in the note form from a radio group of icon buttons (arrow keys move the choice, each has a name for screen readers); and holidays and time off shade the day withbackground-layer-1; the label always comes with the shade. On the phone grid a noted day gets a short bar under its number. The chosen day's notes head its day list. WithonDayNotesChange(notes), the day list offers "Add a day note" and Edit and Remove on each note; the form takes a kind, a label and an optional "Through" date, and Escape cancels. Notes never block moving projects onto a day. - Deadlines. Pass
deadlines({ id, kind, date, title, brand, href, late, met }, withkindfromMonthCalendar.deadlineKinds:publishordraft) to show each project's publish date and draft due date next to the working-date chips. In the grid a deadline is a single quiet line with no fill: a 12px icon (publishorfile-text), the word ("Publishes:" or "Draft due:") and the title, insubduedat 11px, after that day's chips. It always carries words, never color alone. Deadlines are fixed dates, so they cannot be dragged or moved with Alt and an arrow; change them on the project. Setlatewhen the date has passed and the step is not done: the icon becomesalert-triangleand the line turnsnegative-content, and screen readers hear "late". Setmetonce the step is done: it reads "Published" or "Draft in" with a check. Deadlines share the day's room with chips and fall into "2 more" first. The day list shows the chosen day's deadlines under its notes, each with the title, the word and the brand. Withhrefevery deadline is a link (on Home it opens the project drawer); without one it picks its day. On the phone grid a day with a deadline gets a small outlined diamond after its dots (red when late), and the date button's label names each deadline. - Week settings.
weekStartis any day, 0 (Sunday) to 6 (Saturday).dayspicks which weekdays show, so[1, 2, 3, 4, 5]is a working week; hidden days drop out of the grid and Alt with Left or Right skips them. Follow the person's own setting. - Put it in a Card or panel on
background-elevated, full width. Below about 860px the day list wraps under the grid.
- Two kinds of day note.
time-off(shades the day, and Home takes it out of the week) andcustom, shown as Note, for everything else. Holidays are notes with theflagicon; notes saved asholidayread that way.onDayDoubleClick(date, cell)fires on a double click on the day itself (not on a chip), after moving focus to the day's date; anchor a Popover to the cell for a quick add. GiveonAdd(date, cell)too, so the day list's add button opens the same quick add: that is the keyboard and phone path, since a double click is neither.
- Readable at a glance. Day numbers are 15px, weekday names 13px, chip titles 13px bold (up to three lines, breaking between words) and brands 12px; days are 144px tall. A project chip puts its title on the full width and the stage mark beside the brand underneath.
- Deadlines read Draft Due and Publish (Roger's words, October 20, 2026; once done, "Draft in" and "Published"). With
onDeadlineMovethey move like projects: drag one to another day, or focus it and press Alt with an arrow key; each move is announced ("Draft Due for FX5 launch video moved to Monday, October 26").
- Day notes in the grid are quiet italic text in the subdued gray (it holds 4.5:1 in both themes; not an opacity, which would not), with no icon, each in a small outlined box on
background-elevatedunder the date. They stack, one per line, and wrap to as many lines as they need (the row grows to fit, like chips). The day list shows the icon and the details. Time off still shades the day. - Notes over a run of days (a
dateand a laterendDate, not repeating) are one bar through those days, the way a calendar shows an all day event: drawn once per week row from its first day, one line with an ellipsis, and in the same lane on every day it covers so bars never cross. A bar that carries on from last week or into next week runs to the edge with a square end. Each day it covers keeps room for it above its own notes and chips. Repeating notes fall on one day each, so they stack like single notes. - Workdays. Pass
workDays(0 Sunday to 6 Saturday, the profile's workdays) and the other shown days getbackground-rest, a shade darker than the calendar in both themes, so the working week stands out. The phone grid shades the day the same way. The date and everything on the day keep their colors and contrast. - Repeating notes theme certain days, like a weekly filming day or a monthly payday. The note form has Repeats (Does not repeat, Every week, Every other week, Every month); a repeating note falls on one day each time, from its first day on, and Until (optional) ends it (it replaces Through, which is for a run of days). The day list reads "Every Tuesday until Dec 1". Editing or removing it changes every repeat.
noteFallsOn(note, date)answers whether a note lands on a day, for counting time off elsewhere. - Adding sits in the day list's header as two icons at the top right, beside the count: Add a project (or to-do) and Add a day note, each named for the chosen day for screen readers. The inline field for a new item still opens at the foot of the list.
- The grid has a hairline frame (
divider), and the gap between the grid and the day list is 16. - The chosen day is a soft gray fill (
neutral-subtle), not an outline; today keeps its blue circle. On phones the chosen date gets a gray circle. - Opening an item. With
onItemClick, clicking a chip (or Enter on it) picks its day and calls it with the item; Makado opens the project's drawer. Cards in the day list open as a whole too. - Notes have no picture icons. One plain icon per kind shows in the day list (a note, or time off);
MonthCalendar.noteIconsstays only for older data. - Removing a repeating note asks in its row, the way a calendar does: This day (that day is skipped,
skip), This and later (it ends the day before), or All. Escape or Cancel keeps it. - Phone strip. With
strip(oncompactwithscroll: 'weeks') the calendar shows one week. It scrolls sideways under your finger (the weeks before and after sit beside it) and snaps a week at a time, with no scrollbar; the chosen weekday stays chosen and the new week is announced. With the keyboard, focus the strip and press Left or Right. The title row keeps only the month title and a quiet Today: no arrows, so the strip leads. The month title is a button with a chevron: tapping it opens the scrolling month view, tapping again (or picking a day) folds it back to the week. Today brings this week back. - Week list only. With
agendaView: 'week'the day list always shows the chosen day's week, with no switch. Each day with something on it gets its heading, notes, deadlines and items; the chosen day always shows (its heading reads stronger), and the add field and the note form open in its place. Add a project and Add a day note sit in the week header and act on the chosen day. - Phone list, pinned. On the Calendar page the list under the week runs edge to edge and down to the bottom of the screen (rounded top corners;
--gv-cal-agenda-bottomkeeps the last card clear of the tab bar). Its cards are bigger and calmer: 16px titles, 13px brand and status, more padding and space between them, and no format line. - Day notes pick their day. The note form has a Day field (it starts on the chosen day), so a note can go on any day, even from the week list. On phones the form opens in a bottom sheet (New day note, Edit day note).
- Touch: in the day or week list, press and hold a card, then drop it on a day in the calendar above (the week strip, or the opened month) to move it there. The day under your finger lights up.
- Scrolling weeks hold still. In
scroll: 'weeks'days are not shaded as another month (the month in view changes while you scroll), the scroll settles on a week, and hover fills only show on devices that hover. - Options on a card. Pass
onItemHold(item, anchor)to offer an item's options (open, move to another day, change its stage). On touch, hold a card and let go without moving; holding and moving still drags it to a day. A right click or the context menu key (Shift F10) opens the same options. - Week list on phones. With
agendaView: 'week'andcompact, the chosen day leads the list; busy days earlier in the week fold into an Earlier this week row (with how many items) that opens them. Choosing another day folds them again. - With
onReorder, items also change priority: drag one above or below another (in the day list or on a chip), or press Alt Shift with up or down. Dropped on an item on another day, it moves to that day in that place. Items show in the order they're given, so pass them most important first. Each move is announced. headActionsputs extra controls in the title row before the month arrows: on the Calendar page, a search that narrows what's shown. Keep them icon buttons until opened.agendaTitleputs a title over the day list in the month's type, on a row level with the month's, so the list and the grid start together.onAgendaToggleadds the hide or show button: it keeps the top right corner either way (over the list while it shows, at the end of the month's row while hidden), and only its arrow turns.