Skip to content

lydiacodesdaily/just-today

Repository files navigation

JustToday · Routine Execution Engine

A neurodivergent-friendly routine execution app for iOS and Web. Not a habit tracker or planner — a calm, judgment-free guide that adapts to the energy you actually have today and helps you get through your routines one step at a time.

2 platforms shipped (iOS + Web) · 44 unit tests · Built solo (design + engineering)

Case Study → · Live App →


The Problem

Planning isn't the bottleneck. Starting is.

Habit trackers reward streaks. Planners optimize for scheduling. Todo apps fill up with items that never get touched. These tools assume the hard part is remembering what to do — but for many neurodivergent users, the hard part is initiating.

Existing tools also assume consistent capacity. They don't account for the fact that the same person has a Low day and a Flow day and needs their tool to adapt, not judge.

JustToday doesn't ask what you planned. It asks how you're feeling today and guides you from there.


Engineering Architecture

Three interlocking systems, built from first principles

1. Energy-Adaptive Routine Engine

Each task in a routine template carries three boolean flags: lowIncluded, steadyIncluded, flowIncluded. Selecting an energy level each morning filters the task list dynamically via a pure deriveTasksForPace() function. A Low day shows only essentials; a Flow day surfaces bonus tasks. The template is never mutated — the run adapts at runtime.

2. Timestamp-Based Timer (no drift)

The timer uses absolute Unix timestamps (startedAt, plannedEndAt) rather than interval counters, so it survives app backgrounding and system interruptions without drift. Remaining time is computed from Date.now() on each tick — never accumulated from setInterval.

All state transitions are pure functions:

(currentRun: RoutineRun, ...args) => RoutineRun

startRun, pauseRun, resumeRun, skipTask, extendTask, moveTask, addQuickTask, endRun — no side effects, fully testable, easy to replay in debugging.

One nuance worth noting: extendTask gives fresh time from zero rather than adding to the remaining balance. If you're two minutes overdue and tap +5m, you get a full five minutes of positive time — not three. This is a deliberate product decision: the system never punishes you for being slow.

3. GTD-Informed Capture System

Partway through building, I noticed the energy + routine system felt incomplete for users with a lot of open loops floating in their heads. Reading Getting Things Done mid-build surfaced what was missing: a trusted capture system. JustToday now includes a full GTD-inspired layer: Areas → Projects → Focus Items, a brain dump inbox, weekly intents, check-ins, and a guides library. GTD's capture and clarify phases, adapted for users who need low-friction entry over rigid structure.


Two codebases. One engine.

iOS and Web are separate codebases rather than a shared monorepo package — a deliberate pragmatic choice for v1 that keeps each platform free to optimize without coordination overhead.

State management:

  • Mobile: React Context + useState, auto-saved to AsyncStorage on every mutation
  • Web: Zustand with persist middleware + localStorage
  • Both call the same pure engine functions — divergence is only in how state is held, not in the logic itself

One meaningful web-only behavior: on run completion or abandonment, syncRunToTemplate() writes task edits made during the run back to the source template. The mobile app does not do this yet — an intentional scope decision, not an oversight.

Abandoned runs from the same calendar day can be resumed via resumeAbandonedRun(), which resets status to notStarted and restores the task queue from where the user left off.

Platform-aware audio: Audio resolves by platform file extension: soundEngine.web.ts uses the Web Audio API; the native version uses expo-av. Metro and Next.js each auto-resolve the correct file. An audio queue prevents overlapping announcements.


Tech Stack

Layer Mobile Web
Framework React Native + Expo Next.js 16
Routing Expo Router (file-based) App Router
State React Context + AsyncStorage Zustand + localStorage
Audio expo-av + expo-speech Web Audio API + Web Speech API
Language TypeScript (strict) TypeScript (strict)
Tests Jest (44 unit tests across both platforms)

Neurodivergent-First Design Decisions

Every interaction is designed to reduce cognitive load, not add to it:

  • Progressive disclosure — The run screen surfaces 2 primary actions (Pause / Done with this), not 8. Extension options reveal only when tapped.
  • Supportive language — "How are you feeling today?" not "Select mode." "Done with this →" not "Skip." Overtime displays as +MM:SS with the message: "It's okay to take your time."
  • No streaks, no guilt — Energy is selected fresh each day. Optional items expire at midnight — no rollover pressure, no record of failure.
  • Accessible by default — 44pt+ touch targets, VoiceOver/TalkBack labels on all interactive elements, letter-spacing tuned for dyslexic users.

Project Structure

just-today/
├── app/                    # Expo Router screens
│   ├── (tabs)/
│   │   ├── index.tsx       # Home / Today screen
│   │   └── settings.tsx
│   ├── routine/
│   │   ├── [id].tsx        # Template editor
│   │   └── run.tsx         # Active run screen (no back gesture)
│   └── _layout.tsx
├── src/
│   ├── engine/             # Pure state transition functions (fully tested)
│   ├── models/             # TypeScript interfaces
│   ├── audio/              # Platform-resolved audio (soundEngine.web.ts / native)
│   ├── persistence/        # AsyncStorage wrappers
│   ├── context/            # React contexts
│   ├── hooks/              # Custom hooks
│   └── components/         # UI components
├── web/                    # Next.js app (separate codebase)
│   └── src/
│       ├── engine/         # Same pure functions, web-adapted
│       ├── stores/         # Zustand stores (run, focus, routine, area, project…)
│       └── app/            # App Router pages
└── assets/
    └── sounds/

Getting Started

Prerequisites: Node.js 18+, Xcode (for iOS simulator)

npm install
npm start       # Expo dev server
npm run ios     # iOS simulator

Web:

cd web
npm install
npm run dev

Initial setup:

  1. Add assets/sounds/tick.mp3 and assets/sounds/chime.mp3
  2. Launch the app → create a routine template
  3. Tag tasks as Low / Steady / Flow as needed
  4. Select your energy mode → tap Start

Running Tests

npm test

44 unit tests cover the core run engine across both platforms — state transitions, timer logic, overtime calculation, and pace-based task filtering.


Built by

Lydia Kwag — Senior Front-End & Product Engineer lydiakwag.com · lydiacodesdaily

Also building: Flow Club Companion · FlowMate


MIT License · No analytics. No accounts. No judgment. Just today.

About

Energy-aware routine execution engine

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages