Skip to content

Repository files navigation

Aurea Focus

Angular TypeScript RxJS SCSS PWA

A premium Pomodoro timer built with Angular 18 — luxury-minimalist design, reactive state management, and offline-first PWA support.


At a glance (for reviewers & recruiters)

Aurea Focus is a personal productivity application built to practice modern Angular patterns and to serve my own day-to-day focus needs. Every technical decision was made with production quality in mind: reactive state with BehaviorSubject streams, clean service-to-component data flow, zero backend dependency, and a fully installable PWA.

What it demonstrates

  • Reactive state management: All state lives in injectable services exposed as Observable streams via BehaviorSubject. Components subscribe, react, and clean up properly — no shared mutable globals.
  • Clean service architecture: Each concern is isolated — TimerService owns the countdown, StatisticsService owns persistence and streak logic, ThemeService owns appearance, NotificationService owns alerts. Components are thin orchestrators.
  • Adaptive business logic: StatisticsService applies a time-of-day productivity weighting (morning sessions score higher), auto-resets daily counters at midnight, and maintains a cross-session streak via localStorage.
  • PWA-first: Service Worker enabled, offline-capable, installable on desktop and mobile with no friction.
  • Luxury UI: Cormorant Garamond serif display font, gold accent system, conic-gradient progress ring, smooth cubic-bezier entrance animations, and automatic dark mode after 18:00.

Stack: Angular 18 · TypeScript 5.4 · RxJS 7.8 · SCSS · Angular PWA · Web Audio API


Table of contents


Architecture

aurea-focus/
└── src/app/
    ├── components/        # Presentational UI components
    │   ├── timer/         # Main countdown ring + controls
    │   ├── stats/         # Productivity metrics panel
    │   ├── notes/         # Session notes panel
    │   └── settings/      # Configuration modal
    ├── services/          # State & business logic (singleton)
    ├── models/            # TypeScript interfaces & enums
    └── app.*              # Root component + bootstrap

State is never shared directly between components. Components communicate through services only — a deliberate choice to keep the dependency graph flat and testable.

Services (state layer)
    │
    ├── BehaviorSubject<T>    → holds the current value
    ├── Observable<T>         → exposed to components (read-only view)
    └── methods               → the only way to mutate state
         │
         ▼
    Components (UI layer)
         │
         ├── subscribe to observable streams
         ├── render current state
         └── call service methods on user interaction

Tech stack

Technology Version Role
Angular 18 SPA framework
TypeScript 5.4 Language
RxJS 7.8 Reactive streams / state management
SCSS Latest Styling (design tokens via CSS variables)
Angular PWA n/a Service Worker + manifest
Web Audio API n/a In-browser sound notifications (no assets needed)

Project structure

src/app/
├── components/
│   ├── timer/
│   │   ├── timer.component.ts        # Mode switching, controls, completion handler
│   │   ├── timer.component.html      # Mode selector, SVG ring, control buttons
│   │   └── timer.component.scss      # Ring animation, responsive layout
│   ├── stats/
│   │   ├── stats.component.ts        # Subscribes to StatisticsService
│   │   ├── stats.component.html      # Four metric cards
│   │   └── stats.component.scss
│   ├── notes/
│   │   ├── notes.component.ts        # Note CRUD, mode-aware tagging
│   │   ├── notes.component.html      # Collapsible input + note list
│   │   └── notes.component.scss
│   └── settings/
│       ├── Settings.component.ts     # Settings form, persists on save
│       ├── Settings.component.html   # Modal overlay with form controls
│       └── Settings.component.scss
├── services/
│   ├── timer.service.ts              # Countdown engine, mode management, settings persistence
│   ├── statistics.service.ts         # Session tracking, streak, productivity score
│   ├── notes.service.ts              # Notes CRUD + localStorage persistence
│   ├── theme.service.ts              # Light/dark toggle + auto dark mode
│   └── notification.service.ts       # Toast notifications + Web Audio beep
├── models/
│   ├── timer.model.ts                # TimerMode enum, TimerSettings, TimerState
│   ├── stats.model.ts                # Statistics, SessionRecord
│   └── note.model.ts                 # Note interface
├── app.ts                            # Root component, layout, settings modal trigger
├── app.html                          # Three-panel layout (stats | timer | notes)
├── app.config.ts                     # Angular providers (router, HTTP)
└── main.ts                           # Bootstrap entry point

Data flow

Session completion (end-to-end example)

TimerService (interval ticks)
    │
    ├─→ timeLeft reaches 0
    ├─→ calls this.pause()
    ├─→ increments sessionsCompleted if mode === WORK
    └─→ emits updated TimerState via BehaviorSubject
         │
         ▼
    TimerComponent (subscribed to timerState$)
         │
         ├─→ detects wasRunning && !state.isRunning && timeLeft === 0
         └─→ calls onTimerComplete()
              │
              ├─→ NotificationService.showNotification(message)
              ├─→ NotificationService.playSound()    (if soundEnabled)
              └─→ StatisticsService.addSession(mode, duration, true)
                   │
                   ├─→ updates todaySessions, totalTime, productivity
                   ├─→ calls updateStreak()
                   └─→ persists to localStorage
                        │
                        ▼
                   StatsComponent (subscribed to stats$)
                        └─→ re-renders metric cards

Timer logic

TimerService drives the countdown with an RxJS interval(1000) subscription. Key design decisions:

Mode switching is guarded: changeMode() is a no-op if the timer is running. The HTML template disables mode buttons accordingly ([disabled]="timerState.isRunning").

Settings apply immediately when the timer is stopped; a running timer ignores settings changes mid-session.

Auto-transition (autoStartBreaks setting): after each session, a 3-second delay fires autoTransition() which selects the next mode (long break every 4 sessions, short break otherwise) and calls start() automatically.

Progress is a derived value computed on demand: ((totalTime − timeLeft) / totalTime) × 100. The SVG ring consumes this as a CSS custom property (--progress).

Modes:
  WORK        → default 25 min
  SHORT_BREAK → default 5 min   (after sessions 1, 2, 3)
  LONG_BREAK  → default 15 min  (after every 4th session)

Statistics & productivity scoring

StatisticsService tracks four metrics, all persisted in localStorage:

Metric Description
todaySessions Work sessions completed today; resets at midnight
totalTime Cumulative work minutes this week
currentStreak Consecutive days with at least one session
productivity Adaptive score (0–100), starts at 95

Productivity weighting: sessions completed between 09:00–12:00 add +2 points; sessions between 14:00–17:00 add +1 point. The score is capped at 100.

Streak logic: on each session save, the date is compared to lastSessionDate in localStorage. Yesterday → streak increments. Any earlier → streak resets to 1. checkDailyReset() polls every 60 seconds and zeroes todaySessions when the clock crosses midnight.


Theme system

ThemeService manages a 'light' | 'dark' value written to document.documentElement as a data-theme attribute. SCSS reads design tokens from CSS variables keyed on this attribute.

Auto dark mode: on service construction, if the current hour is ≥ 18 or < 6 and no stored preference exists, dark mode is activated automatically.

Manual toggle: toggleTheme() flips the current value and persists it to localStorage under aureaFocusTheme, overriding the auto rule for the remainder of the session.


Notes system

NotesService maintains an ordered list of Note objects (newest first). Each note captures:

interface Note {
  id: number;        // Date.now() at creation time — doubles as unique key
  content: string;
  timestamp: string; // Formatted locale string displayed in the UI
  mode: string;      // Timer mode active when the note was saved
}

Notes are tagged with the active timer mode so you can see at a glance whether a note was written during a focus block or a break. All CRUD operations flush synchronously to localStorage.


Notifications

Toast: NotificationService.showNotification(message) pushes a NotificationMessage to a BehaviorSubject. The root component subscribes and renders the toast overlay. It auto-dismisses after 3 seconds via setTimeout.

Audio alert: NotificationService.playSound() synthesises a short 800 Hz sine-wave beep using the Web Audio API — no audio file assets required. The oscillator ramps its gain from 0.3 to ~0 over 500 ms for a clean fade-out.


Design system

Color palette

Token Light Dark
Primary #1a1a1a #f5f5f5
Accent (gold) #d4af37 #ffd700
Background #ffffff #1a1a1a
Surface #f8f8f8 #2a2a2a

Typography

  • Display / timer: Cormorant Garamond (serif) — precision and luxury
  • UI / labels: Montserrat (sans-serif) — legible at small sizes

Key animations

Animation Element Timing
Slide down Header 0.8s cubic-bezier
Fade in up Timer panel 1s cubic-bezier
Fade in left / right Side panels 1s cubic-bezier
Progress ring Conic gradient 1s linear
Logo rotation App icon 60s continuous

PWA

Aurea Focus is configured as an installable Progressive Web App:

  • ngsw-config.json defines the Service Worker caching strategy
  • manifest.webmanifest provides app name, icons, and standalone display mode
  • All state is in localStorage — the app is fully functional offline after the first load
  • Responsive layout works on mobile and desktop without a separate build target

Configuration

Default settings (editable at runtime via the Settings modal):

Setting Default Range
Work duration 25 min 1–60 min
Short break 5 min 1–30 min
Long break 15 min 1–60 min
Sound enabled Boolean
Auto-start breaks Boolean
Deep Focus Mode Boolean

Settings are persisted immediately on save and take effect from the next session — a running timer is never interrupted by a settings change mid-countdown.


LocalStorage schema

Key Type Description
aureaFocusSettings TimerSettings JSON Work/break durations and feature toggles
aureaFocusStats Statistics JSON Session counts, streak, productivity score
aureaFocusNotes Note[] JSON All saved session notes
aureaFocusTheme 'light' | 'dark' User theme preference
lastSessionDate string (date) Reference date for streak calculation

No backend, no account — all data is local and private.


Running locally

Prerequisites: Node.js 18+ · npm 9+

# Clone the repository
git clone <repo-url>
cd aurea-focus

# Install dependencies
npm install

# Start the development server
npm start
# → http://localhost:4200

# Production build
npm run build
# → dist/aurea-focus/browser/

No environment variables or external services required.


Browser compatibility

Browser Version Status
Chrome 90+ ✅ Fully Supported
Firefox 88+ ✅ Fully Supported
Safari 14+ ✅ Fully Supported
Edge 90+ ✅ Fully Supported

Future Enhancements

Potential improvements for learning:

  • Unit tests (Jasmine/Karma)
  • E2E tests (Cypress/Playwright)
  • Service Worker for offline capability
  • Web Push Notifications API
  • i18n internationalization
  • Data export/import (JSON)
  • Cloud sync (Firebase/Supabase)
  • WCAG 2.1 AA accessibility compliance
  • Analytics integration
  • Dark mode system preference detection

Screenshots

Light Theme

  • Clean, elegant interface with golden accents
  • Animated progress ring
  • Real-time statistics

Dark Theme

  • Automatically activates 6 PM - 6 AM
  • Warm gold highlights on dark background
  • Reduced eye strain for night sessions

Motivation

Created as a personal learning project to:

  1. Practice modern Angular development (v18+)
  2. Explore reactive programming with RxJS
  3. Build a production-quality PWA from scratch
  4. Solve my own concentration challenges at work
  5. Demonstrate clean architecture principles

Personal project — public — no restrictive license.

About

A premium Pomodoro app designed for deep focus, clarity, and sustainable productivity.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages