# ZeroClock UI Polish & UX Upgrade — Design ## Problem The first redesign pass established a Swiss/Dieter Rams foundation but went too far into monochrome territory. The app is a wall of near-black grey with zero personality. The signature amber accent color is completely absent. All buttons look identical. Empty states are sad grey text. `alert()` calls for all feedback. No visual hierarchy for primary actions. The app also stores data in AppData (not portable) and doesn't remember window position/size. ## Goals 1. Reintroduce warm amber (#D97706) as the strategic accent color 2. Lift the entire background palette from near-black to charcoal 3. Establish clear button hierarchy (primary/secondary/ghost/danger) 4. Replace all `alert()` calls with a toast notification system 5. Design rich empty states with icons, copy, and CTA buttons 6. Add amber focus states on all inputs 7. Add UI zoom control in Settings (persistent) 8. Make the app fully portable (data next to exe) 9. Persist window position and size between runs --- ## Design System ### Color Palette (Revised) ``` Background layers (lifted from near-black to charcoal): --bg-base: #1A1A18 app background --bg-surface: #222220 cards, panels, navRail, titlebar --bg-elevated: #2C2C28 hover, active, raised elements --bg-inset: #141413 inputs, recessed areas Text (warm whites — unchanged): --text-primary: #F5F5F0 headings, active items --text-secondary: #8A8A82 body, descriptions --text-tertiary: #5A5A54 disabled, placeholders (bumped from #4A4A45) Borders (bumped to match brighter backgrounds): --border-subtle: #2E2E2A dividers, card borders --border-visible: #3D3D38 input borders, focused Accent (NEW — amber): --accent: #D97706 button fills, active indicators --accent-hover: #B45309 hover/pressed amber --accent-muted: rgba(217,119,6,0.12) subtle glows, backgrounds --accent-text: #FBBF24 amber text on dark backgrounds Status (semantic — unchanged): --status-running: #34D399 timer active, success, toggle on --status-warning: #EAB308 pending, caution --status-error: #EF4444 destructive, overdue --status-info: #3B82F6 informational ``` Remove all legacy alias colors (--color-amber mapped to white, --color-background, --color-surface, etc.). ### Button Hierarchy **Primary** — amber fill, for the ONE main action per view: - `bg-accent text-[#1A1A18] font-medium rounded` - Hover: `bg-accent-hover` - Used: Start timer, Create Project, Save Settings, Generate Report, Apply Filters, Create Invoice **Secondary** — outlined, for supporting actions: - `border border-border-visible text-text-primary rounded` - Hover: `bg-bg-elevated` - Used: Export CSV, Export Data, Cancel buttons, Clear filters **Ghost** — text only, for low-priority actions: - `text-text-secondary hover:text-text-primary` - No border, no background - Used: "View all" links, tab navigation inactive **Danger** — destructive actions: - `border border-status-error text-status-error rounded` - Hover: `bg-status-error/10` - Used: Clear Data, Delete buttons in confirmation dialogs ### Input Focus States All inputs, selects, and textareas: - Current: `focus:border-border-visible` (grey, barely visible) - New: `focus:border-accent focus:outline-none` with `box-shadow: 0 0 0 2px rgba(217,119,6,0.12)` (amber border + subtle amber glow) --- ## Shell ### TitleBar - "ZEROCLOCK" wordmark: `text-accent-text` (amber #FBBF24) instead of `text-text-secondary` - Running timer section: unchanged (green dot + project + time + stop) - Window controls: unchanged ### NavRail - Active indicator: 2px left border in `bg-accent` (#D97706) instead of `bg-text-primary` (white) - Active icon color: stays `text-text-primary` (white) - Tooltip: add subtle caret/triangle pointing left for polish - Timer dot at bottom: stays green (semantic) --- ## View Designs ### Dashboard **Header:** - Greeting: "Good morning/afternoon/evening" in `text-lg text-text-secondary` - Date: "Monday, February 17, 2026" in `text-xs text-text-tertiary` **Stats row (4 stats):** - This Week | This Month | Today | Active Projects - Labels: `text-xs text-text-tertiary uppercase tracking-[0.08em]` - Values: `text-[1.25rem] font-mono text-accent-text` (amber) **Weekly chart:** - Bar fill: `#D97706` (amber), today's bar: `#FBBF24` (lighter amber) - Grid: `#2E2E2A`, ticks: `#5A5A54` **Recent entries:** - Flat list as current - "View all" ghost link bottom-right, navigates to Entries **Empty state:** - Centered vertically in available space - Lucide `Clock` icon, 48px, `text-text-tertiary` - "Start tracking your time" — `text-text-secondary` - "Your dashboard will come alive with stats, charts, and recent activity once you start logging hours." — `text-xs text-text-tertiary` - Primary amber button: "Go to Timer" ### Timer **Hero display:** - `text-[3rem] font-mono text-text-primary` centered - When running: colon separators pulse amber (`text-accent-text` with opacity animation) - When stopped: all white, static **Start/Stop button:** - Start: `bg-accent text-[#1A1A18] px-10 py-3 text-sm font-medium rounded` - Stop: `bg-status-error text-white px-10 py-3 text-sm font-medium rounded` (filled red) - 150ms color transition between states **Inputs:** - `max-w-[36rem] mx-auto` (centered) - Amber focus states - Project select: small colored dot preview of selected project color **Recent entries:** - Scoped to today's entries - Most recent entry: subtle amber left border - Max 5, "View all" ghost link **Empty state:** - Lucide `Timer` icon, 40px, `text-text-tertiary` - "No entries today" — `text-text-secondary` - "Select a project and hit Start to begin tracking" — `text-xs text-text-tertiary` ### Projects **Header:** - Title "Projects" left - "+ Add" becomes small amber primary button: `bg-accent text-[#1A1A18] px-3 py-1.5 text-xs font-medium rounded` **Cards:** - 2px left border in project color - `bg-bg-surface hover:bg-bg-elevated` transition - Hover: left border widens from 2px to 3px - Rate and client inline: "ClientName · $50.00/hr" **Create/Edit dialog:** - Submit: amber primary - Cancel: secondary - Color picker: row of 8 preset swatches above hex input - Presets: #D97706, #3B82F6, #8B5CF6, #EC4899, #10B981, #EF4444, #06B6D4, #6B7280 **Empty state:** - Lucide `FolderKanban` icon, 48px, `text-text-tertiary` - "No projects yet" — `text-text-secondary` - "Projects organize your time entries and set billing rates for clients." — `text-xs text-text-tertiary` - Primary amber button: "Create Project" (opens dialog) ### Entries **Filter bar:** - Wrapped in `bg-bg-surface rounded p-4` container - "Apply": amber primary - "Clear": ghost button - `mb-6` gap between filter bar and table **Table:** - Header row: `bg-bg-surface` background - Duration column: `text-accent-text font-mono` (amber) - Edit/delete hover reveal: unchanged **Empty state (below filter bar):** - Lucide `List` icon, 48px, `text-text-tertiary` - "No entries found" — `text-text-secondary` - "Time entries will appear here as you track your work. Try adjusting the date range if you have existing entries." — `text-xs text-text-tertiary` - Primary amber button: "Go to Timer" ### Reports **Filter bar:** - Same `bg-bg-surface rounded p-4` container - "Generate": amber primary - "Export CSV": secondary **Stats:** - `mt-6` spacing after filter bar - Values: `text-accent-text font-mono` (amber) **Chart:** - Bars use each project's own assigned color - Fallback for no-color projects: #D97706 - Grid: `#2E2E2A`, ticks: `#5A5A54` **Breakdown:** - Hours value: `text-accent-text font-mono` - Earnings: `text-text-secondary font-mono` **Empty state:** - Lucide `BarChart3` icon, 48px, `text-text-tertiary` - "Generate a report to see your data" — `text-text-secondary` - No CTA button (Generate button is right there) ### Invoices **Tabs:** - Active: `border-b-2 border-accent text-text-primary` (amber underline) - Inactive: `text-text-tertiary hover:text-text-secondary` **List table:** - Header row: `bg-bg-surface` - Amount: `text-accent-text font-mono` - Status colors: unchanged (semantic) **Create form:** - Submit: amber primary - Cancel: secondary - Total line: `text-accent-text font-mono` **Invoice detail dialog:** - Export PDF: amber primary - Total: `text-accent-text` **Empty state:** - Lucide `FileText` icon, 48px, `text-text-tertiary` - "No invoices yet" — `text-text-secondary` - "Create invoices from your tracked time to bill clients." — `text-xs text-text-tertiary` - Primary amber button: "Create Invoice" (switches to Create tab) ### Settings **Buttons:** - "Save Settings": amber primary - "Export": secondary - "Clear Data": danger (red) **Toggle:** stays green when active (semantic "on" state) **All `alert()` calls:** replaced with toasts **UI Zoom control (NEW):** - New section "Appearance" between Timer and Data sections - Label: "UI Scale" - Minus button [-] | value display "100%" | Plus button [+] - Steps: 80%, 90%, 100%, 110%, 120%, 130%, 150% - Implementation: CSS `zoom` property on the `#app` root element - Persisted via `update_settings('ui_zoom', '100')` in the settings SQLite table - Applied on app startup before first paint (read from settings store in App.vue onMounted) --- ## Toast Notification System ### Component: `ToastNotification.vue` - Fixed position, top-center of the main content area, `top-4` - Max width 320px - `bg-bg-surface border border-border-subtle rounded shadow-lg` - 3px left border colored by type: - Success: `border-status-running` (green) - Error: `border-status-error` (red) - Info: `border-accent` (amber) - Content: Lucide icon (Check/X/Info, 16px) + message in `text-sm text-text-primary` - Enter animation: slide down from -20px + fade, 200ms - Exit: fade out 150ms - Auto-dismiss: 3 seconds - Click to dismiss early - Stack with 8px gap, max 3 visible ### Store: `useToastStore` - `addToast(message: string, type: 'success' | 'error' | 'info')` - Auto-generates unique ID - Auto-removes after 3s timeout - Max 3 toasts visible, oldest removed first ### Replacements All `alert()` calls across the app become toast calls: - Settings save success/failure - Clear data success/failure - Export data success/failure - Timer "Please select a project" - Reports "Please select a date range" - Reports "No data to export" - Invoices "Please select a client" --- ## Portable App ### Problem Currently uses `directories::ProjectDirs` to store the SQLite database in the OS app data directory (e.g., `C:\Users\\AppData\Roaming\ZeroClock\`). This is not portable. ### Solution Change `get_data_dir()` in `lib.rs` to always resolve relative to the executable: ```rust fn get_data_dir() -> PathBuf { let exe_path = std::env::current_exe().unwrap(); let data_dir = exe_path.parent().unwrap().join("data"); std::fs::create_dir_all(&data_dir).ok(); data_dir } ``` This stores `data/timetracker.db` next to the `.exe`. The `directories` crate dependency can be removed from Cargo.toml. --- ## Window State Persistence ### Plugin: `tauri-plugin-window-state` Add the Tauri window-state plugin to save/restore: - Window position (x, y) - Window size (width, height) - Maximized state ### Implementation 1. Add dependency: `tauri-plugin-window-state = "2"` to Cargo.toml 2. Add `"window-state"` to plugins in tauri.conf.json 3. Register plugin: `.plugin(tauri_plugin_window_state::Builder::new().build())` in lib.rs 4. The plugin auto-saves state to a `.window-state` file. Since we're making the app portable, configure the plugin to store state in our `data/` directory next to the exe. ### Config In `tauri.conf.json`, add to plugins: ```json "window-state": { "all": true } ``` Remove `"center": true` from the window config so the saved position is respected on subsequent launches. --- ## Transitions & Motion Unchanged from first redesign, plus: - Toast enter: translateY(-20px) + opacity 0 to 0+1, 200ms ease-out - Toast exit: opacity 1 to 0, 150ms - Button hover: 150ms background-color transition - Project card left-border width: 150ms transition on hover - Timer colon amber pulse: opacity 0.4 to 1.0 on accent-text color, 1s ease-in-out infinite (only when running)