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)