๐โโ๏ธ 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"
}
}
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();
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();
});
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;
}
}
๐จ 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);
}
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();
}
});
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);
}
}
});
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;
}
}
๐ 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'
};
๐ 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
- Start with Manifest V3 - V2 is deprecated
- Use Chrome APIs properly - Alarms > setInterval
- Design matters - Users judge extensions by UI
- Privacy is crucial - Be transparent about data usage
- 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)