DEV Community

Mostafa Salama
Mostafa Salama

Posted on

🏃‍♂️ Building a Health-Focused Chrome Extension: StandUp Clock with Smart Reminders

🏃‍♂️ Building a Health-Focused Chrome Extension: StandUp Clock with Smart Reminders

From idea to Chrome Web Store - My journey building a productivity tool that cares about your health


💡 The Problem That Started It All

As developers, we've all been there - hours pass by in what feels like minutes when you're deep in the code zone. Before you know it, you've been sitting for 4+ hours straight, your back aches, and your health is paying the price.

After completing my digital clock project (part of my #100ProjectsChallenge), I realized something was missing: a tool that doesn't just show time, but actively helps you stay healthy while being productive.

That's how StandUp Clock was born - a Chrome extension that combines a beautiful digital clock with intelligent health reminders.

🎯 What We Built

StandUp Clock is more than just another productivity extension. Here's what makes it special:

✅ Beautiful Digital Clock - Always visible in your browser toolbar

✅ Smart Health Reminders - Configurable stand-up notifications

✅ Customizable Settings - Personalize reminder frequency and messages

✅ Glassmorphism Design - Modern, clean interface

✅ Manifest V3 Ready - Built with the latest Chrome extension standards

✅ Privacy-First - All data stays local, no tracking

🔗 Try it live | [⭐ GitHub]


🏗️ Technical Architecture Deep Dive

Manifest V3 Implementation

First challenge: Chrome's Manifest V3. If you're still using V2, it's time to upgrade! Here's our manifest structure:

{
  "manifest_version": 3,
  "name": "StandUp Clock - Health Reminder",
  "version": "1.0.0",
  "description": "A beautiful digital clock extension that reminds you to stand up and move for better health.",

  "permissions": [
    "notifications",
    "alarms", 
    "storage"
  ],

  "background": {
    "service_worker": "background.js"
  },

  "action": {
    "default_popup": "popup.html",
    "default_title": "StandUp Clock"
  }
}
Enter fullscreen mode Exit fullscreen mode

Key decisions:

  • No activeTab permission - We don't need to interact with web pages
  • Service worker instead of background page - Manifest V3 requirement
  • Minimal permissions - Only what we actually need

Service Worker Magic

The heart of our extension lives in background.js. Here's how we handle the core functionality:

// background.js - Service Worker
import { NotificationService } from './services/NotificationService.js';
import { AlarmService } from './services/AlarmService.js';

class StandUpClockService {
    constructor() {
        this.notificationService = new NotificationService();
        this.alarmService = new AlarmService();
        this.initialize();
    }

    initialize() {
        // Set up alarm listeners
        chrome.alarms.onAlarm.addListener(this.handleAlarm.bind(this));

        // Set up installation handler
        chrome.runtime.onInstalled.addListener(this.handleInstall.bind(this));

        // Set up settings change listener
        chrome.storage.onChanged.addListener(this.handleStorageChange.bind(this));
    }

    async handleAlarm(alarm) {
        if (alarm.name === 'standUpReminder') {
            await this.showStandUpReminder();
        }
    }

    async showStandUpReminder() {
        const settings = await this.getSettings();

        const notification = {
            type: 'basic',
            iconUrl: 'icons/icon48.png',
            title: '🏃‍♂️ Time to Stand Up!',
            message: settings.reminderMessage || 'Take a break and move around for better health!',
            buttons: [
                { title: '✅ Done!' },
                { title: '⏰ Remind me in 5 min' }
            ]
        };

        await this.notificationService.create('standUpReminder', notification);
    }

    async scheduleNextReminder() {
        const settings = await this.getSettings();
        const intervalMinutes = settings.reminderInterval || 60;

        chrome.alarms.create('standUpReminder', {
            delayInMinutes: intervalMinutes
        });
    }
}

// Initialize the service
new StandUpClockService();
Enter fullscreen mode Exit fullscreen mode

Popup Interface with Real-Time Clock

The popup shows a beautiful digital clock with quick settings access:

// popup.js - Real-time Clock Display
class ClockPopup {
    constructor() {
        this.clockElement = document.getElementById('digital-clock');
        this.dateElement = document.getElementById('current-date');
        this.is24Hour = true;
        this.startClock();
    }

    startClock() {
        this.updateTime();
        setInterval(() => this.updateTime(), 1000);
    }

    updateTime() {
        const now = new Date();

        // Format time based on user preference
        const timeString = this.is24Hour 
            ? now.toLocaleTimeString('en-US', { 
                hour12: false, 
                hour: '2-digit', 
                minute: '2-digit', 
                second: '2-digit' 
              })
            : now.toLocaleTimeString('en-US', {
                hour12: true,
                hour: 'numeric',
                minute: '2-digit',
                second: '2-digit'
              });

        this.clockElement.textContent = timeString;

        // Update date
        const dateString = now.toLocaleDateString('en-US', {
            weekday: 'long',
            year: 'numeric',
            month: 'long',
            day: 'numeric'
        });

        this.dateElement.textContent = dateString;
    }
}

document.addEventListener('DOMContentLoaded', () => {
    new ClockPopup();
});
Enter fullscreen mode Exit fullscreen mode

Settings Management with Chrome Storage

User preferences are stored using Chrome's storage API:

// settings.js - Persistent Settings
class SettingsManager {
    constructor() {
        this.defaultSettings = {
            reminderInterval: 60, // minutes
            reminderMessage: 'Time to stand up and stretch!',
            soundEnabled: true,
            autoStart: true,
            workingHours: {
                start: '09:00',
                end: '17:00',
                enabled: false
            }
        };
    }

    async loadSettings() {
        return new Promise((resolve) => {
            chrome.storage.sync.get(this.defaultSettings, (settings) => {
                resolve(settings);
            });
        });
    }

    async saveSettings(settings) {
        return new Promise((resolve) => {
            chrome.storage.sync.set(settings, () => {
                resolve(true);
            });
        });
    }

    async resetToDefaults() {
        await this.saveSettings(this.defaultSettings);
        return this.defaultSettings;
    }
}
Enter fullscreen mode Exit fullscreen mode

🎨 Design Philosophy: Glassmorphism Meets Functionality

The visual design was crucial - this extension needs to be beautiful enough that users actually want to see it every day.

CSS Architecture

/* Modern glassmorphism design */
.popup-container {
    width: 320px;
    min-height: 240px;
    background: linear-gradient(135deg, 
        rgba(255, 255, 255, 0.1),
        rgba(255, 255, 255, 0.05)
    );
    backdrop-filter: blur(10px);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 16px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
}

.digital-clock {
    font-family: 'JetBrains Mono', monospace;
    font-size: 2.8rem;
    font-weight: 600;
    text-align: center;
    color: #ffffff;
    text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    margin: 1.5rem 0;
}

.quick-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
    margin: 1rem;
}

.action-btn {
    padding: 0.75rem 1rem;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 8px;
    color: rgba(255, 255, 255, 0.9);
    transition: all 0.3s ease;
}

.action-btn:hover {
    background: rgba(255, 255, 255, 0.2);
    transform: translateY(-1px);
}
Enter fullscreen mode Exit fullscreen mode

Responsive Design Considerations

Since Chrome extensions have fixed popup sizes, every pixel counts:

  • Compact layout that shows essential info immediately
  • Clear visual hierarchy with proper spacing
  • Readable typography even at small sizes
  • Intuitive icons for quick recognition

🔧 Key Technical Challenges & Solutions

Challenge 1: Service Worker Lifecycle

Problem: Service workers can be terminated at any time, making timer management tricky.

Solution: Use Chrome's alarms API instead of setInterval:

// ❌ Don't do this in service workers
setInterval(() => {
    showReminder(); // Will stop working when SW terminates
}, 60000);

// ✅ Do this instead
chrome.alarms.create('standUpReminder', {
    delayInMinutes: 60,
    periodInMinutes: 60
});

chrome.alarms.onAlarm.addListener((alarm) => {
    if (alarm.name === 'standUpReminder') {
        showReminder();
    }
});
Enter fullscreen mode Exit fullscreen mode

Challenge 2: Notification Interaction

Problem: Users need to respond to notifications effectively.

Solution: Action buttons with smart logic:

chrome.notifications.onButtonClicked.addListener(async (notificationId, buttonIndex) => {
    if (notificationId === 'standUpReminder') {
        if (buttonIndex === 0) { // "Done!" button
            await scheduleNextReminder();
            await chrome.notifications.clear(notificationId);
        } else if (buttonIndex === 1) { // "Snooze 5 min" button
            chrome.alarms.create('standUpSnooze', {
                delayInMinutes: 5
            });
            await chrome.notifications.clear(notificationId);
        }
    }
});
Enter fullscreen mode Exit fullscreen mode

Challenge 3: Settings Synchronization

Problem: Settings need to sync across devices and persist.

Solution: Chrome storage sync with fallbacks:

async function getSettings() {
    try {
        // Try sync storage first
        const settings = await chrome.storage.sync.get(defaultSettings);
        return settings;
    } catch (error) {
        // Fallback to local storage
        console.warn('Sync storage failed, using local:', error);
        const settings = await chrome.storage.local.get(defaultSettings);
        return settings;
    }
}
Enter fullscreen mode Exit fullscreen mode

📊 Performance & Privacy Considerations

Minimal Resource Usage

  • Small bundle size: ~50KB total
  • Efficient background script: Only runs when needed
  • Smart alarm scheduling: Uses browser's native timers
  • Local storage only: No external API calls

Privacy-First Approach

// Everything stays local - no data collection
const privacyPolicy = {
    dataCollection: 'none',
    tracking: 'disabled',
    storage: 'local-only',
    permissions: 'minimal-required'
};
Enter fullscreen mode Exit fullscreen mode

🚀 Chrome Web Store Journey

Submission Checklist

Getting approved required careful attention to:

✅ High-quality icons (16x16, 32x32, 48x48, 128x128)

✅ Professional screenshots (1280x800 or 640x400)

✅ Clear description with value proposition

✅ Privacy policy explaining data handling

✅ Single purpose compliance

✅ Permission justification for each API used

Store Optimization

Title: "StandUp Clock - Health Reminder"

Description: Focus on health benefits and productivity

Keywords: health, productivity, reminder, clock, wellness

Screenshots: Show both clock and notification features


🔮 What's Next?

Planned Features (v2.0)

  • 📊 Health analytics - Track standing habits
  • 👥 Team features - Synchronized team breaks
  • 🎵 Custom notification sounds
  • ⚡ Integration with fitness trackers
  • 🌙 Smart scheduling based on calendar

Technical Improvements

  • Progressive Web App version
  • Better accessibility features
  • Multi-language support
  • Advanced customization options

💻 Want to Build Your Own Extension?

Key Takeaways for Aspiring Extension Developers

  1. Start with Manifest V3 - V2 is deprecated
  2. Use Chrome APIs properly - Alarms > setInterval
  3. Design matters - Users judge extensions by UI
  4. Privacy is crucial - Be transparent about data usage
  5. Test thoroughly - Extensions have unique constraints

Resources That Helped Me


🎯 Try StandUp Clock Today!

Ready to improve your health while coding?

🔗 GitHub Repository - Full source code

📦 Chrome Web Store - Install directly ( coming soon )


💝 Supporting Open Source Development

Building quality tools takes time and effort. If StandUp Clock helps improve your health and productivity, consider supporting continued development:

☕ Buy me a coffee - One-time support

🎯 Become a patron - Monthly support for ongoing updates

Your support helps me:

  • Add requested features faster
  • Create more health-focused tools
  • Keep everything free & open source

- Build the developer community

Tags: #chromeextension #javascript #health #productivity #opensource #webdevelopment #manifestv3 #100daysofcode

Top comments (0)