Live: https://ripple-iota-olive.vercel.app
A fun, modern hydration tracking web app. The core experience is a visual water bottle on the home screen that fills up as you log water throughout the day.
- Animated water bottle — SVG bottle with a live wave surface and ripple ring animation on each log
- Quick-log buttons — 250ml, 500ml, 750ml, or a custom amount
- Configurable daily goal — presets at 1.5L, 2L, 2.5L, 3L, 3.5L plus a custom input
- Undo — remove your last logged entry
- Streak counter — tracks consecutive days you hit your goal, displayed as a flame badge
- Celebration screen — confetti burst and overlay when you hit your daily goal
- Section markers — dashed lines at 25%, 50%, 75%, and 100% fill on the bottle
- Persistent state — all data lives in localStorage via redux-persist, no account needed
- Midnight reset — daily intake automatically resets when you open the app on a new day
| Layer | Choice | Reason |
|---|---|---|
| Framework | Next.js (App Router) | File-based routing, server components, great PWA support |
| Language | TypeScript | End-to-end type safety across store, components, and hooks |
| Styling | Tailwind CSS v4 + raw CSS | Tailwind for layout, raw CSS for bottle wave animations |
| Animation | Framer Motion | Spring-based transitions, modal slide-ups, celebration screen |
| State | Redux Toolkit (RTK) | Clean slice structure, great DevTools, ready to travel to React Native |
| Persistence | redux-persist → localStorage | Zero-config localStorage sync, no backend required |
| Confetti | canvas-confetti | Lightweight, no dependencies |
| Fonts | DM Serif Display + DM Sans | Premium lifestyle aesthetic via next/font/google |
| Deployment | Vercel | Zero-config, deploy on git push |
app/
├── components/
│ ├── Bottle.tsx # SVG bottle + wave animation + ripple rings
│ ├── LogButtons.tsx # Quick-add buttons, custom input, undo
│ ├── GoalModal.tsx # Bottom sheet goal editor
│ ├── Celebration.tsx # Confetti + goal reached overlay
│ └── StoreProvider.tsx # Redux Provider + PersistGate + midnight reset
├── store/
│ ├── index.ts # configureStore + redux-persist
│ ├── hooks.ts # Typed useAppDispatch / useAppSelector
│ ├── listenerMiddleware.ts # Goal-reached → celebration + streak side effect
│ └── slices/
│ ├── hydrationSlice.ts # drunk, goal, history, goalReached
│ ├── streakSlice.ts # count, lastActive
│ ├── settingsSlice.ts # unit, reminderGap, quietHours
│ └── uiSlice.ts # modalOpen, celebrating (not persisted)
├── globals.css # Design tokens, wave keyframes, Tailwind import
├── layout.tsx # Root layout with fonts and StoreProvider
└── page.tsx # Main screen
{
hydration: {
drunk: number, // ml consumed today
goal: number, // daily target in ml
history: { amount: number, timestamp: number }[],
goalReached: boolean,
},
streak: {
count: number,
lastActive: string, // 'YYYY-MM-DD'
},
settings: {
unit: 'ml' | 'oz',
reminderGap: number, // minutes between reminders
quietHours: { start: number, end: number },
},
ui: {
modalOpen: boolean,
celebrating: boolean, // blacklisted from persist
}
}npm install
npm run devOpen http://localhost:3000 in your browser.
- Palette: calm coastal blues (
#3A9ECC,#1E6F99,#EBF6FB) with warm accents (#F4A261orange,#52B788green) - Typography: DM Serif Display (italic) for the wordmark, DM Sans for UI
- Aesthetic: premium lifestyle app — mobile-first, max-width 400px, designed to feel native
- No sign-up wall — the app is usable in under 5 seconds from a cold start
- Smart reminders via Web Push API + service worker
- Weekly history and trend chart
- Shareable streak card
- Dynamic goal calculator based on weight and activity level
- Bottle skins and themes unlocked by streaks
- User accounts and cross-device sync (Supabase)
- Apple Health / Google Fit sync (native app only)