# Grocy2 Design Specification > A modern, delightful household management interface for Grocy ## Table of Contents 1. [Vision & Goals](#vision--goals) 2. [User Research & Personas](#user-research--personas) 3. [Design Principles](#design-principles) 4. [Information Architecture](#information-architecture) 5. [Dashboard Design](#dashboard-design) 6. [Component Library](#component-library) 7. [Responsive Strategy](#responsive-strategy) 8. [Interaction Patterns](#interaction-patterns) 9. [Visual Design System](#visual-design-system) 10. [Accessibility](#accessibility) 11. [Performance Budget](#performance-budget) 12. [Siri & Shortcuts Integration](#siri--shortcuts-integration) 13. [Technical Architecture](#technical-architecture) 14. [Implementation Phases](#implementation-phases) --- ## Vision & Goals ### Vision Statement Grocy2 transforms household inventory management from a chore into a seamless part of daily life. It anticipates needs, rewards good habits, and stays out of the way when everything is running smoothly. ### Primary Goals 1. **Reduce food waste** - Surface expiring items before they go bad 2. **Eliminate "what do we need?" confusion** - Smart shopping lists that sync 3. **Enable meal decisions** - Know what you can cook with what you have 4. **Work for couples/families** - Multi-user, real-time sync 5. **Voice-first option** - Siri integration for hands-free kitchen use ### Success Metrics - Time to complete common action: < 3 seconds - First contentful paint: < 1 second - User returns to app daily - Food waste reduction (self-reported) --- ## User Research & Personas ### Primary Persona: The Household Manager **Name:** Sarah & Tony (shared responsibility couple) **Context:** - Both work, limited time for household admin - Shop 2-3x per week at different stores - Cook 4-5 dinners at home per week - Have experienced food waste frustration - Use iPhones, occasionally iPad in kitchen **Pain Points:** - "Did we already buy milk?" - "What's going bad that we need to use?" - "I'm at the store but forgot what we needed" - "I want to cook but don't know what we can make" **Jobs to Be Done:** 1. When I'm leaving the store, I want to know if I forgot anything 2. When I'm cooking, I want to mark ingredients as used without friction 3. When I'm planning the week, I want to see what we have and what we need 4. When something is expiring, I want to be reminded before it's too late ### Usage Contexts | Context | Device | Attention | Primary Need | |---------|--------|-----------|--------------| | Kitchen (cooking) | Phone/iPad | Partial | Quick consume, recipe check | | Store (shopping) | Phone | Focused | Shopping list, barcode scan | | Couch (planning) | iPad/Phone | Full | Meal planning, inventory review | | On-the-go | Phone | Minimal | Quick add to list, voice command | --- ## Design Principles ### 1. Context-Aware The interface adapts to time of day and user behavior. Morning shows different priorities than evening. Shopping mode looks different than home mode. ### 2. Progressive Disclosure Simple surface, power underneath. The most common actions are obvious. Advanced features are discoverable but not overwhelming. ### 3. Forgiving Every action is undoable. Nothing is permanent. The system assumes good intent and makes recovery easy. ### 4. Glanceable Status is visible in 1 second. Action takes 2 seconds. Exploration takes 3+. The dashboard is not a wall of text. ### 5. Rewarding Completing tasks feels good. Subtle animations, haptic feedback, and visual confirmation make the mundane satisfying. ### 6. Calm Technology When everything is fine, the app is quiet. It only demands attention when something needs attention. No anxiety-inducing red badges for non-urgent items. --- ## Information Architecture ### Navigation Structure ``` ├── Dashboard (/) │ ├── Pulse Status │ ├── Smart Cards (contextual) │ └── Quick Actions │ ├── Inventory (/stock) │ ├── All Items (searchable, filterable) │ ├── By Location (Fridge, Pantry, Freezer) │ ├── By Category │ └── Expiring Soon │ ├── Shopping (/shopping) │ ├── Current List │ ├── Shopping Mode (focused checkout) │ └── List History │ ├── Recipes (/recipes) │ ├── All Recipes │ ├── Can Make Now │ ├── Meal Planning │ └── Recipe Detail │ └── Settings (/settings) ├── Connection ├── Preferences ├── Locations ├── Categories └── About ``` ### Data Model (from Grocy API) ``` Stock ├── Products (master list) ├── Stock Entries (actual inventory) ├── Locations (Fridge, Pantry, etc.) ├── Quantity Units (pcs, liters, kg) └── Product Groups (Dairy, Produce, etc.) Shopping ├── Shopping Lists (can have multiple) └── Shopping List Items Recipes ├── Recipes ├── Recipe Ingredients └── Meal Plan ``` --- ## Dashboard Design ### Layout Structure ``` ┌─────────────────────────────────────┐ │ Status Bar (native) │ ├─────────────────────────────────────┤ │ │ │ ◯ Pulse Ring ◯ │ │ (ambient status glow) │ │ │ ├─────────────────────────────────────┤ │ │ │ ┌─────────────────────────┐ │ │ │ Smart Card 1 │ │ │ │ (highest priority) │ │ │ └─────────────────────────┘ │ │ │ │ ┌─────────────────────────┐ │ │ │ Smart Card 2 │ │ │ └─────────────────────────┘ │ │ │ │ ┌─────────────────────────┐ │ │ │ Smart Card 3 │ │ │ └─────────────────────────┘ │ │ │ ├─────────────────────────────────────┤ │ [+ Add] [📷 Scan] [🎤 Voice] │ │ Quick Action Bar │ └─────────────────────────────────────┘ ``` ### Pulse Ring Component **Purpose:** Ambient awareness of household status at a glance. **States:** - `healthy` - Soft green glow, gentle pulse. All systems nominal. - `attention` - Amber glow, slightly faster pulse. Items expiring in 1-3 days. - `urgent` - Red glow, prominent pulse. Expired items or critical stock. **Interaction:** - Tap to expand into list of items needing attention - Ring shows count badge when collapsed **Technical:** - CSS animation with `@keyframes` - Color transitions via CSS custom properties - Reduced motion support for accessibility ### Smart Cards Cards appear based on relevance and urgency. Maximum 4 cards visible; scroll for more. #### Card Priority Algorithm ``` Priority Score = Base Weight + Urgency Modifier + Time Modifier Base Weights: - Expired items: 100 - Expiring today: 90 - Shopping list (non-empty): 70 - Low stock items: 60 - Expiring this week: 50 - Recipe suggestions: 40 Time Modifiers: - Morning (6-10am): +20 to expiring, +10 to shopping - Midday (10am-2pm): +30 to shopping - Evening (4-8pm): +30 to recipes, +20 to expiring - Weekend: +20 to meal planning Context Modifiers: - Shopping list > 5 items: +20 to shopping - Nothing expiring: -50 to expiring card (hide) ``` #### Card Types **1. Expiring Soon Card** ``` ┌─────────────────────────────────────┐ │ 🕐 Use Soon 3 ▾ │ ├─────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────┐ │ │ │ Milk Tomorrow [→] │ │ │ └─────────────────────────────────┘ │ │ ┌─────────────────────────────────┐ │ │ │ Spinach 2 days [→] │ │ │ └─────────────────────────────────┘ │ │ ┌─────────────────────────────────┐ │ │ │ Greek Yogurt 3 days [→] │ │ │ └─────────────────────────────────┘ │ │ │ │ See all 7 items → │ └─────────────────────────────────────┘ ``` **Swipe Actions:** - Left swipe: "Used" - Consume 1 unit, animate out - Right swipe: "Replace" - Add to shopping list **2. Shopping List Card** ``` ┌─────────────────────────────────────┐ │ 🛒 Shopping List 12 ▾ │ ├─────────────────────────────────────┤ │ │ │ ○ Eggs (dozen) │ │ ○ Bread │ │ ○ Olive oil │ │ ○ Chicken breast │ │ +8 more... │ │ │ │ [ 🛍 Start Shopping Trip ] │ └─────────────────────────────────────┘ ``` **Interactions:** - Tap item to check off - "Start Shopping Trip" enters focused shopping mode - Badge shows count **3. Low Stock Card** ``` ┌─────────────────────────────────────┐ │ 📉 Running Low 4 ▾ │ ├─────────────────────────────────────┤ │ │ │ Coffee ██░░░░░░ 2 bags │ │ Rice █░░░░░░░ 1 bag │ │ Pasta ██░░░░░░ 2 boxes │ │ Olive Oil █░░░░░░░ ¼ bottle │ │ │ │ [ Add All to List ] │ └─────────────────────────────────────┘ ``` **4. Recipe Suggestions Card** ``` ┌─────────────────────────────────────┐ │ 🍳 Tonight's Options 5 ▾ │ ├─────────────────────────────────────┤ │ │ │ ┌─────────┐ │ │ │ 🍝 │ Pasta Carbonara │ │ │ │ ✓ Have everything │ │ └─────────┘ │ │ ┌─────────┐ │ │ │ 🥗 │ Caesar Salad │ │ │ │ Missing: croutons │ │ └─────────┘ │ │ │ │ Browse Recipes → │ └─────────────────────────────────────┘ ``` ### Quick Action Bar Fixed to bottom of screen in thumb-reach zone. ``` ┌─────────────────────────────────────┐ │ │ │ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │ + │ │ 📷 │ │ 🎤 │ │ │ │ Add │ │Scan │ │Voice│ │ │ └─────┘ └─────┘ └─────┘ │ │ │ └─────────────────────────────────────┘ ``` **Add Button:** - Opens bottom sheet with: - Recent items (last 10 added) - Search field - Quick category buttons - "New Product" option **Scan Button:** - Opens camera for barcode - Recognizes product → shows add/consume dialog - Unknown barcode → offers to create product **Voice Button:** - Activates speech recognition - Shows live transcription - Parses intent: - "Add milk to shopping list" → adds item - "Used 2 eggs" → consumes from stock - "What's expiring?" → shows expiring list --- ## Component Library ### Design Tokens ```css /* Colors */ --color-primary: #6B8E6B; /* Sage green */ --color-primary-light: #8FAF8F; --color-primary-dark: #4A6B4A; --color-accent: #E8927C; /* Soft coral */ --color-accent-light: #F4B8A8; --color-accent-dark: #C75B39; --color-urgent: #C75B39; /* Terracotta */ --color-warning: #E4A853; /* Amber */ --color-success: #6B8E6B; /* Green */ --color-background: #FAFAF8; /* Warm white */ --color-surface: #FFFFFF; --color-surface-elevated: #FFFFFF; --color-text-primary: #2D3436; /* Warm charcoal */ --color-text-secondary: #636E72; --color-text-tertiary: #9CA3A8; --color-border: #E8E8E6; --color-border-light: #F0F0EE; /* Spacing */ --space-xs: 4px; --space-sm: 8px; --space-md: 16px; --space-lg: 24px; --space-xl: 32px; --space-2xl: 48px; /* Typography */ --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; --font-size-xs: 12px; --font-size-sm: 14px; --font-size-md: 16px; --font-size-lg: 18px; --font-size-xl: 24px; --font-size-2xl: 32px; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; --line-height-tight: 1.2; --line-height-normal: 1.5; --line-height-relaxed: 1.75; /* Border Radius */ --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-full: 9999px; /* Shadows */ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07); --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15); /* Transitions */ --transition-fast: 150ms ease; --transition-normal: 250ms ease; --transition-slow: 350ms ease; /* Z-Index Scale */ --z-dropdown: 100; --z-sticky: 200; --z-overlay: 300; --z-modal: 400; --z-toast: 500; ``` ### Core Components #### Button Variants: `primary`, `secondary`, `ghost`, `danger` Sizes: `sm`, `md`, `lg` ```tsx ``` #### Card ```tsx } badge={3} onExpand={() => {}} > {children} ``` #### SwipeableRow ```tsx , color: 'success', onSwipe: handleUse }} rightAction={{ icon: , color: 'primary', onSwipe: handleAdd }} > ``` #### BottomSheet ```tsx ``` #### PulseRing ```tsx ``` --- ## Responsive Strategy ### Breakpoints ```css /* Mobile first */ --bp-sm: 640px; /* Large phones, small tablets */ --bp-md: 768px; /* Tablets portrait */ --bp-lg: 1024px; /* Tablets landscape, small laptops */ --bp-xl: 1280px; /* Desktops */ --bp-2xl: 1536px; /* Large desktops */ ``` ### Layout Adaptations #### Mobile (< 640px) - Single column layout - Bottom navigation bar - Full-screen modals - Swipe gestures primary - Cards stack vertically - Quick action bar fixed to bottom #### Tablet (640px - 1024px) - Two-column card grid - Bottom navigation or side tabs - Slide-over sheets instead of full modals - Split view for list + detail - Floating action button option #### Desktop (> 1024px) - Persistent sidebar navigation - Three-column layout possible - Modal dialogs (not full screen) - Hover states and tooltips - Keyboard shortcuts - Command palette (⌘K) - Drag and drop ### Touch Targets - Minimum touch target: 44x44px (Apple HIG) - Comfortable spacing between targets: 8px minimum - Thumb zone consideration for mobile --- ## Interaction Patterns ### Gestures | Gesture | Context | Action | |---------|---------|--------| | Pull down | Any list | Refresh data | | Swipe left | List item | Primary action (use/delete) | | Swipe right | List item | Secondary action (add to list) | | Long press | List item | Context menu | | Pinch | Recipe photo | Zoom | | Shake | Anywhere | Undo last action | ### Keyboard Shortcuts (Desktop) | Shortcut | Action | |----------|--------| | `/` or `⌘K` | Open command palette | | `n` | New item | | `s` | Focus search | | `1-5` | Navigate to section | | `Escape` | Close modal/cancel | | `⌘Z` | Undo | | `⌘⇧Z` | Redo | | `Enter` | Confirm/submit | | `↑/↓` | Navigate list | | `Space` | Toggle selected item | ### Feedback Patterns **Optimistic Updates:** - UI updates immediately - Background sync with server - Rollback on error with toast notification **Loading States:** - Skeleton screens for initial load - Inline spinners for actions - Pull-to-refresh indicator **Success Feedback:** - Checkmark animation - Subtle color flash - Haptic feedback (mobile) - Item animates out of list **Error Handling:** - Inline error messages - Toast for system errors - Retry option always available - Never lose user input --- ## Visual Design System ### Color Usage **Primary (Sage Green):** - Navigation active states - Primary buttons - Success states - "Healthy" status **Accent (Coral):** - Call-to-action buttons - Highlights and badges - Important notifications **Urgent (Terracotta):** - Expired items - Critical alerts - Delete confirmations **Neutral Palette:** - Background, cards, borders - Text hierarchy - Disabled states ### Typography Scale ``` Display: 32px / 700 / 1.2 - Page titles (rare) Heading 1: 24px / 600 / 1.2 - Section headers Heading 2: 18px / 600 / 1.3 - Card titles Body: 16px / 400 / 1.5 - Default text Body Small: 14px / 400 / 1.5 - Secondary text Caption: 12px / 400 / 1.4 - Metadata, timestamps ``` ### Iconography - Use Lucide icons (already in project) - Consistent 24px size in navigation - 20px in buttons and list items - 16px for inline icons - 2px stroke weight ### Motion Principles 1. **Purposeful** - Motion guides attention and confirms actions 2. **Quick** - 150-300ms for most transitions 3. **Natural** - Spring physics, not linear 4. **Reducible** - Respect `prefers-reduced-motion` **Standard Easings:** ```css --ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* Enter */ --ease-in: cubic-bezier(0.7, 0, 0.84, 0); /* Exit */ --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); /* Move */ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* Bounce */ ``` --- ## Accessibility ### Requirements - WCAG 2.1 AA compliance minimum - Screen reader support (VoiceOver, TalkBack) - Keyboard navigation complete - Color contrast 4.5:1 minimum for text - Focus indicators visible - Reduced motion support - Touch targets 44x44px minimum ### Implementation - Semantic HTML elements - ARIA labels where needed - Skip navigation link - Focus management for modals - Announce dynamic content changes - Alt text for images - Form labels and error messages --- ## Performance Budget ### Targets | Metric | Target | Max | |--------|--------|-----| | First Contentful Paint | < 1s | 1.5s | | Largest Contentful Paint | < 2s | 2.5s | | Time to Interactive | < 3s | 4s | | Cumulative Layout Shift | < 0.1 | 0.15 | | First Input Delay | < 50ms | 100ms | ### Bundle Size Budget | Asset | Target | Max | |-------|--------|-----| | Initial JS | < 100KB | 150KB | | Initial CSS | < 20KB | 30KB | | Total initial | < 150KB | 200KB | | Lazy chunks | < 50KB each | 75KB | ### Strategies - Code splitting by route - Lazy load below-fold content - Image optimization (WebP, lazy loading) - Preconnect to API server - Service worker for caching - Virtual lists for long inventories --- ## Siri & Shortcuts Integration ### Approach Since this is a web app, native Siri integration requires a companion approach: 1. **PWA with URL Schemes** - Handle `grocy://` URLs 2. **Shortcuts Integration** - Create iOS Shortcuts that call our web app 3. **Future: Native Wrapper** - Capacitor/React Native for App Intents ### URL Scheme Design ``` grocy://add?item=milk&list=shopping grocy://consume?item=eggs&amount=2 grocy://open?page=shopping grocy://open?page=expiring ``` ### Shortcuts Templates **"Add to Shopping List"** ``` 1. Ask for input: "What do you want to add?" 2. Open URL: grocy://add?item=[input]&list=shopping ``` **"What's Expiring?"** ``` 1. Open URL: grocy://open?page=expiring ``` **"Start Shopping Trip"** ``` 1. Open URL: grocy://open?page=shopping&mode=trip ``` ### Voice Input (Web Speech API) For in-app voice commands: ```tsx const recognition = new webkitSpeechRecognition(); recognition.continuous = false; recognition.interimResults = true; // Parse intents from transcription const intents = { add: /add (.+) to (shopping list|stock)/i, consume: /used? (\d+)? ?(.+)/i, query: /do we have (.+)/i, expiring: /what'?s expiring/i, }; ``` --- ## Technical Architecture ### State Management **Zustand Store Structure:** ```typescript interface AppState { // Connection isConnected: boolean; serverUrl: string | null; // User user: User | null; systemInfo: SystemInfo | null; // Data stock: StockProduct[]; products: Product[]; locations: Location[]; quantityUnits: QuantityUnit[]; productGroups: ProductGroup[]; shoppingList: ShoppingListItem[]; recipes: Recipe[]; // UI State dashboardCards: CardConfig[]; isShoppingMode: boolean; // Actions connect: (url: string, key: string) => Promise; disconnect: () => void; refreshAll: () => Promise; // Stock actions consumeProduct: (id: number, amount: number) => Promise; addToStock: (id: number, amount: number, options?: StockOptions) => Promise; // Shopping actions addToShoppingList: (productId: number, amount: number) => Promise; checkOffItem: (itemId: number) => Promise; clearCompletedItems: () => Promise; } ``` ### Data Fetching Strategy - **React Query** for server state - Automatic background refetching - Optimistic updates for mutations - Stale-while-revalidate pattern - Offline support with persistence ### Folder Structure ``` src/ ├── api/ │ ├── client.ts # API client │ └── hooks.ts # React Query hooks ├── components/ │ ├── ui/ # Base components │ │ ├── Button.tsx │ │ ├── Card.tsx │ │ ├── Input.tsx │ │ └── ... │ ├── dashboard/ # Dashboard-specific │ │ ├── PulseRing.tsx │ │ ├── SmartCard.tsx │ │ ├── QuickActionBar.tsx │ │ └── ... │ ├── stock/ # Stock page components │ ├── shopping/ # Shopping components │ └── layout/ # Layout components ├── hooks/ │ ├── useVoiceInput.ts │ ├── useSwipeGesture.ts │ └── ... ├── lib/ │ ├── cardPriority.ts # Card sorting algorithm │ ├── voiceParser.ts # Voice command parsing │ └── utils.ts ├── pages/ │ ├── Dashboard.tsx │ ├── Stock.tsx │ ├── Shopping.tsx │ ├── Recipes.tsx │ └── Settings.tsx ├── store/ │ └── index.ts # Zustand store ├── styles/ │ ├── tokens.css # Design tokens │ └── global.css ├── types/ │ └── grocy.ts # TypeScript types ├── App.tsx └── main.tsx ``` --- ## Implementation Phases ### Phase 1: Foundation (Week 1) **Goal:** Design system and responsive shell - [ ] Set up design tokens (CSS custom properties) - [ ] Create base UI components (Button, Card, Input, etc.) - [ ] Implement responsive layout shell - [ ] Create bottom navigation for mobile - [ ] Create sidebar navigation for desktop - [ ] Set up React Query for data fetching - [ ] Dark mode support (CSS variables) **Deliverable:** Navigable app shell that looks good on all devices ### Phase 2: Smart Dashboard (Week 2) **Goal:** Context-aware dashboard with smart cards - [ ] Implement PulseRing component - [ ] Create SmartCard component system - [ ] Build card priority algorithm - [ ] Implement ExpiringCard with swipe actions - [ ] Implement ShoppingListCard - [ ] Implement LowStockCard - [ ] Implement RecipeSuggestionsCard - [ ] Add time-based card prioritization - [ ] Create QuickActionBar **Deliverable:** Functional smart dashboard ### Phase 3: Quick Actions (Week 3) **Goal:** Friction-free adding and consuming - [ ] Build AddItem bottom sheet - [ ] Implement recent items list - [ ] Add product search with typeahead - [ ] Implement barcode scanning (camera API) - [ ] Add voice input (Web Speech API) - [ ] Build voice command parser - [ ] Implement optimistic updates - [ ] Add undo/redo support **Deliverable:** Can add/consume items in < 3 seconds ### Phase 4: Full Pages (Week 4) **Goal:** Complete Stock, Shopping, Recipes pages - [ ] Stock page with filtering and search - [ ] Stock item detail view - [ ] Shopping page with list management - [ ] Shopping mode (focused checkout experience) - [ ] Recipes page with filtering - [ ] Recipe detail with ingredient availability - [ ] Settings page **Deliverable:** All pages functional ### Phase 5: Polish & Native (Week 5) **Goal:** PWA, Siri integration, final polish - [ ] PWA manifest and icons - [ ] Service worker for offline - [ ] URL scheme handlers - [ ] Shortcuts integration documentation - [ ] Home screen widgets (if possible via PWA) - [ ] Animation polish - [ ] Performance optimization - [ ] Accessibility audit - [ ] User testing and fixes **Deliverable:** Production-ready app --- ## Appendix ### API Endpoints Reference ``` GET /api/system/info GET /api/users/me GET /api/stock POST /api/stock/products/{id}/add POST /api/stock/products/{id}/consume POST /api/stock/products/{id}/open GET /api/objects/{entity} POST /api/objects/{entity} PUT /api/objects/{entity}/{id} DELETE /api/objects/{entity}/{id} GET /api/recipes GET /api/recipes/{id}/fulfillment ``` ### Grocy Entity Types - products - locations - quantity_units - product_groups - shopping_list - shopping_lists - recipes - recipes_pos - chores - tasks --- *Document Version: 1.0* *Last Updated: January 2025* *Authors: Claude & Tony*