Lay out month calendar grids from a year and month.
A small Typst utility that takes (year, month) and renders a 7-column grid
with correct leading and trailing blank days, sized for handwritten notes.
Designed for personal planners, training logs, and habit trackers — not for
displaying scheduled events (see cineca
for that).
Install
#import "@preview/calendaring:0.1.0": month-grid, year-grid
Basic usage
#import "@preview/calendaring:0.1.0": month-grid
#month-grid(2026, 6)
Renders a Monday-first grid for June 2026, sized 3.5cm × 3.3cm per cell
(tuned for a rotated A4 page). For unrotated portrait layouts, pass
cell-width: 1fr so the grid fills the page width.
Functions
month-grid(year, month, ...)
| Parameter | Type | Default | Notes |
|---|---|---|---|
year |
int | — | Required. |
month |
int | — | Required. 1…12. |
cell-height |
length | 3.3cm |
Height of each day cell. |
cell-width |
length / fraction | 3.5cm |
Width of each day cell. Use 1fr to fit the page width. |
week-start |
str | "mon" |
"mon" or "sun". |
rotated |
bool | false |
Rotate 90° (useful for portrait A4). |
weekday-names |
array of 7 strings | none |
Override header labels for localization. |
header-fill |
color | luma(230) |
Background of the weekday header row. |
today-fill |
color | luma(220) |
Background of the cell matching today. |
today |
datetime or none |
none |
Highlight the matching day, if in this month. |
events |
((datetime, content),) |
() |
Each event renders below the day number in its matching cell. |
week-numbers |
bool | false |
Prepend an ISO 8601 week-number column. |
week-number-width |
length | 0.8cm |
Width of the week-number column when shown. |
stroke |
stroke | 0.5pt |
Cell border. |
inset |
length | 3pt |
Cell padding. |
cell-content |
(datetime) -> content |
built-in | Override per-cell rendering. Receives a datetime. |
year-grid(year, columns: 3, cell-height: 0.55cm, inset: 1pt, month-label-size: 9pt)
Lay out all twelve months of year on one page, three columns by four rows
by default. Composes twelve small month-grid calls.
is-leap-year(year)
Returns true for Gregorian leap years (divisible by 4 but not by 100,
unless also divisible by 400). Exposed for user code that needs the same
predicate the grid uses internally.
Notes
weekday-names, when overridden, must be in the same order asweek-start. Forweek-start: "sun", the array starts with the Sunday label.- ISO 8601 week numbers are always anchored to the row’s Monday, even in Sunday-first layouts.
Inspirations
The API borrows patterns from established LaTeX calendar packages:
eventsparameter — TikZ calendar’s\if (equals: <date>) { ... }conditional rendering.todayhighlight — wallcalendar’s\todaymacro.weekday-names— wallcalendar’s localization hooks.week-numbers— wallcalendar’s ISO 8601 week column.year-grid— TikZ calendar’smonth listlayout.cell-contentreceiving adatetime— TikZ calendar’s date conditions (Monday,weekend).
Examples
| File | What it shows |
|---|---|
basic.typ |
Minimal usage, leap year, Sunday-first weeks, custom-cell rotation. |
workout-log.typ |
Rotated A4 monthly training log with large handwriting cells. |
habit-tracker.typ |
Daily habit checkboxes per cell. |
weekend-shading.typ |
Grey-shade Sat/Sun by inspecting date.weekday() in the callback. |
training-calendar.typ |
events and today highlight — peak/deload/race periodization. |
wall-calendar.typ |
ISO 8601 week-number column in a wall-calendar layout. |
leap-year.typ |
Leap-year handling (Feb 2024 vs Feb 2025) and is-leap-year. |
year-at-a-glance.typ |
All twelve months on one A4 via year-grid. |
Compile any of them locally:
typst compile --root . examples/training-calendar.typ
License
MIT. See LICENSE.