A free, open-source year-at-a-glance planner. See your entire year on one page. You've seen those $50 "Big Calendars" on TikTok — the ones that show your whole year on a giant wall poster. This is that, but digital, free, and portable.
- Year-at-a-glance — All 12 months, all 365 days, one screen
- Multi-day events — Drag to move, click to edit
- Color coding — 6 event colors to categorize your life
- Themes — 6 accent colors for the calendar itself
- Monochrome mode — Shift+click a theme to view all events in one color
- Import/Export — Your data as JSON, take it anywhere
- Local-first — No accounts, no servers, your data stays in your browser
- Zoom controls — Scale to fit any screen size
Everything is stored in your browser's localStorage. There are no accounts, no databases, no servers tracking your events.
Visit thelongview.dev and start planning.
# Clone the repo
git clone https://github.com/yourusername/the-long-view.git
cd the-long-view
# Install dependencies
npm install
# Run locally
npm run dev
# Build for production
npm run buildEverything is stored in your browser's localStorage. There are no accounts, no databases, no servers tracking your events.
What this means:
- ✅ Your data is private — it never leaves your device
- ✅ Works offline once loaded
⚠️ Clearing browser data deletes your calendar⚠️ Different browsers/devices = different calendars
Use Export to download your calendar as a JSON file. This is your backup. Keep it safe.
Use Import to load a JSON file. You can:
- Merge — Add imported events to your existing calendar
- Replace — Clear everything and start fresh with the imported data
{
"version": "1.0",
"exported_at": "2025-01-01T00:00:00Z",
"events": [
{
"id": "evt_001",
"title": "Summer Vacation",
"color": 3,
"start": "2025-07-01",
"duration": 14
}
]
}Event fields:
id— Unique identifier (auto-generated)title— Event namecolor— Number 1-6 mapping to color palettestart— ISO date string (YYYY-MM-DD)duration— Number of days
| Layer | Choice |
|---|---|
| Framework | React 18 + TypeScript |
| Build | Vite |
| Styling | Tailwind CSS v4 |
| UI Components | shadcn/ui |
| Drag & Drop | dnd-kit |
| Storage | localStorage |
src/
├── components/
│ ├── YearGrid.tsx # Main calendar grid
│ ├── EventPill.tsx # Draggable event bars
│ ├── DayPanel.tsx # Side panel for event management
│ ├── ExportImportButtons.tsx
│ ├── ImportModal.tsx
│ └── ui/ # shadcn components
│
├── hooks/
│ └── useEvents.ts # Event CRUD + localStorage sync
│
├── lib/
│ └── dates.ts # Date helper functions
│
├── types/
│ └── index.ts # TypeScript interfaces
│
└── App.tsx # Main app shell + DnD context
App.tsx
The main shell. Handles:
- Year state and navigation
- Theme selection
- Zoom controls
- DnD context wrapper
- Layout (header, grid, footer)
components/YearGrid.tsx
The calendar grid itself. Uses CSS Grid with:
- 100px month column + 31 day columns
- Dynamic cell sizing based on zoom
- Droppable cells for drag-and-drop
- Theme color applied to headers
components/EventPill.tsx
Individual event bars. Handles:
- Multi-day rendering (rounded corners on start/end)
- Draggable via dnd-kit
- Color from event data
components/DayPanel.tsx
Slide-out panel (shadcn Sheet) for:
- Creating new events
- Editing existing events
- Color picker, duration controls
- Delete with confirmation
hooks/useEvents.ts
All event logic:
- CRUD operations (add, update, delete)
- localStorage persistence
- Year filtering
- Lane calculation for overlapping events
- Import functionality
lib/dates.ts
Pure functions:
getDaysInMonth(year, month)— Returns day countisValidDate(year, month, day)— Validates dates (Feb 30 = false)formatDateString(year, month, day)— Returns ISO format
New event fields?
- Update the
Eventinterface intypes/index.ts - Update
DayPanel.tsxform - Update
useEvents.tsif storage logic changes
New themes?
Add to the THEMES array in App.tsx:
{ color: '#HEX', pale: '#PALE_HEX' }Different grid layout?
YearGrid.tsx controls the CSS Grid. The key line:
gridTemplateColumns: `100px repeat(31, ${cellWidth}px)`This is a desktop-first tool. The year grid is intentionally wide — that's the point.
On mobile:
- You can scroll horizontally to see the full year
- Touch interactions work but aren't optimized
- A dedicated mobile view (vertical months) is on the roadmap but not implemented
Works in all modern browsers. Requires:
- CSS Grid
- localStorage
- ES6+
With hundreds of events, the grid may slow down. The lane calculation algorithm is O(n²) in the worst case. For typical personal use (< 100 events/year), this is fine.
Fork it, go wild.
