A minimalistic time blocking application for organizing your day through intuitive drag-and-drop task management.
- Task Creation - Add tasks with custom durations via inline forms
- Drag & Drop Reordering - Intuitive drag-and-drop with SWAP and PUSH operations
- Auto-Calculated Times - Tasks automatically arrange sequentially starting at 8:00 AM
- Lock Tasks - Prevent specific tasks from being moved (e.g., fixed meetings)
- Offline-First - All data persists locally via localStorage
- Overlap Detection - Visual warnings when tasks conflict with locked items
SWAP - Exchange positions when dropped deep into another task
Before: After SWAP:
08:00 Task A (1h) 08:00 Task B (1h)
09:00 Task B (1h) 09:00 Task A (1h)
PUSH - Snap to task boundaries when dropped at the edge
Before: After PUSH:
08:00 Task A (1h) 08:00 Task B (1h)
09:00 Task B (1h) 09:00 Task A (1h)
10:00 Task C (1h) 10:00 Task C (1h)
- Minimalistic - Clean black & white interface with green task blocks
- Notion-Inspired - Simple, focused, distraction-free
- No Clutter - Spacing-based separation, no unnecessary borders
- Accessible - WCAG AA contrast ratios, keyboard navigation
- Node.js 18+ (or Node 20+ recommended)
- npm or yarn
# Clone the repository
git clone <your-repo-url>
cd time_block_app
# Install dependencies
npm install
# Start development server
npm run devThe app will open at http://localhost:3001/ (or another available port)
# Create production build
npm run build
# Preview production build
npm run preview- Click the [+] button between tasks or at the top
- Enter task name (e.g., "Write design docs")
- Enter duration in minutes (e.g., 120 for 2 hours)
- Click "Add Task" or press Enter
SWAP (Exchange Positions):
- Drag a task deep into another task (past 50% mark)
- Tasks exchange positions
- Only the two tasks move
PUSH (Insert and Shift):
- Drag a task to the top portion of another task
- Dragged task takes the target's position
- All tasks below shift down
- Hover over a task block
- Click the lock icon (top-right corner)
- Locked tasks show an orange lock icon and can't be moved
- Other tasks can't be swapped/pushed with locked tasks
- Hover - Subtle shadow appears
- Dragging - Dashed black border, elevated shadow
- Drop Target - Solid black border highlights target
- Overlapping - Red pulsing border indicates conflicts
- React 18 - UI framework
- TypeScript - Type safety
- Vite - Build tool (fast dev server)
- Material-UI (MUI) - Component library
- Emotion - CSS-in-JS styling
- System Fonts - Native, clean typography
- Zustand - Lightweight state management
- localStorage - Offline-first persistence
- Pragmatic Drag and Drop - Atlassian's modern DnD library
- Native browser APIs for performance
- Accessible keyboard navigation
src/
โโโ components/
โ โโโ Timeline.tsx # Main timeline container
โ โโโ TaskBlock.tsx # Draggable task block component
โ โโโ InsertionPoint.tsx # [+] button between tasks
โ โโโ InlineTaskForm.tsx # Task creation form
โ โโโ TimeLabel.tsx # Hour labels (8:00 AM, etc.)
โโโ store/
โ โโโ taskStore.ts # Zustand store with CRUD operations
โโโ utils/
โ โโโ storage.ts # localStorage helpers
โ โโโ timeCalculations.ts # Time formatting & calculations
โ โโโ dragLogic.ts # SWAP/PUSH algorithms
โโโ theme/
โ โโโ theme.ts # MUI theme (colors, typography)
โโโ types/
โ โโโ index.ts # TypeScript interfaces
โโโ App.tsx # Root component
// Base
Background: #FFFFFF (Pure white)
Text Primary: #000000 (Black)
Text Secondary: #666666 (Medium gray)
Accents: #000000 (Black buttons)
// Task Blocks (Alternating Greens)
Green 1: #E8F5E9 // Pastel green
Green 2: #C8E6C9 // Light mint
Green 3: #A5D6A7 // Medium green
Green 4: #81C784 // Med-dark green
Green 5: #66BB6A // Forest green
// Special States
Lock Icon: #FFB74D (Pastel orange)
Overlap: #EF5350 (Red)- Font Family: System fonts (-apple-system, Segoe UI, Roboto)
- Task Title: 18px, weight 600 (semi-bold)
- Duration: 13px, weight 400 (regular)
- Time Labels: 14px, weight 400/600 (focused)
- First task starts at 8:00 AM
- Subsequent tasks follow sequentially
- Times recalculate after every operation
// Configurable threshold (default 0.5 = 50%)
if (dropPosition >= threshold) {
executeSwap() // Exchange positions
} else {
executePush() // Snap to boundary
}- SWAP with locked task โ Operation fails
- PUSH would move locked task โ Snap back
- Insertion between locked tasks โ Overlap warning
- Task editing (click to edit title/duration)
- Task deletion
- Undo/Redo functionality
- Multi-day calendar view
- Recurring tasks
- Categories/tags with color coding
- Time analytics and insights
- Cloud sync (multi-device)
- Calendar integrations (Google Calendar, iCal)
- Keyboard shortcuts (Cmd+N, arrow keys)
- Smooth animations for position changes
- Dynamic timeline expansion during drag
- Task completion tracking
- Dark mode
- Mobile app (React Native)
See DESIGN_DOCS.md for comprehensive design rationale, including:
- Why Pragmatic DnD over @dnd-kit
- Why MUI over Tailwind
- Why Zustand over Redux
- Complete UX interaction model
- All algorithms with examples
- Single-day view - Only shows one day at a time
- No task editing - Can only create and delete
- No undo/redo - Accidental operations can't be undone
- Overlaps allowed - Flagged but not prevented
- Hardcoded 8 AM start - First task always starts at 8:00 AM
- localStorage only - No cloud sync
This is currently a personal project, but suggestions and feedback are welcome!
MIT License - Feel free to use this project as you wish.
-
Add your first task
Click [+] โ "Morning routine" โ 30 minutes โ Add -
Add a few more tasks
Click [+] โ "Deep work session" โ 120 minutes โ Add Click [+] โ "Lunch break" โ 60 minutes โ Add -
Try reordering
Drag "Lunch break" to the top โ PUSH operation Drag "Deep work" into "Morning routine" โ SWAP operation -
Lock a task
Hover over a task โ Click lock icon Try moving other tasks around the locked one -
Data persists
Refresh the page โ Your tasks are still there!
Built with โค๏ธ using React, TypeScript, and Vite
Questions? Check the DESIGN_DOCS.md for detailed documentation.