# Story 12.1: Fix Masonry Grid Drag & Drop and Responsive Layout Status: planning ## Story As a **user**, I want **a responsive masonry grid where notes can be easily dragged and dropped while maintaining their sizes**, so that **I can organize my notes efficiently on any screen size, similar to Google Keep**. ## Acceptance Criteria 1. **Given** a user is viewing notes in the masonry grid, 2. **When** the user drags a note to reorder it, 3. **Then** the system should: - Allow smooth drag and drop of notes without losing their positions - Maintain the exact size (small, medium, large) of each note during drag and after drop - Provide visual feedback during drag (opacity change, placeholder) - Save the new order to the database - Work seamlessly on both desktop and mobile devices 4. **Given** the user is viewing notes on different screen sizes, 5. **When** the browser window is resized, 6. **Then** the system should: - Automatically adjust the number of columns to fit the available width - Display more columns on larger screens (e.g., 2-4 columns on desktop) - Display fewer columns on smaller screens (e.g., 1-2 columns on mobile) - Maintain the masonry layout where items fill available vertical space - Not break the layout or cause overlapping items 7. **Given** notes have different sizes (small, medium, large), 8. **When** the grid is rendered, 9. **Then** the system should: - Respect the size property of each note (small, medium, large) - Display small notes as compact cards - Display medium notes as standard cards - Display large notes as expanded cards - Arrange items in a true masonry pattern (no gaps, items stack vertically) ## Tasks / Subtasks - [x] Analyze current implementation - [x] Review Muuri configuration in masonry-grid.tsx - [x] Check note size handling (small, medium, large) - [x] Identify drag & drop issues - [x] Identify responsive layout issues - [x] Research best practices - [x] Study Google Keep's masonry layout behavior - [x] Research Muuri layout options and responsive configuration - [x] Document optimal settings for responsive masonry grids - [x] Create detailed fix plan - [x] Document all issues found - [x] Create step-by-step correction plan - [x] Define responsive breakpoints - [x] Define note size dimensions - [ ] Implement fixes - [ ] Fix responsive layout configuration - [ ] Fix drag & drop behavior - [ ] Ensure note sizes are properly applied - [ ] Test on multiple screen sizes - [ ] Testing and validation - [ ] Test drag & drop on desktop - [ ] Test drag & drop on mobile - [ ] Test responsive behavior - [ ] Verify note sizes are maintained - [ ] Verify layout matches Google Keep behavior ## Dev Notes ### Problem Analysis **Current Implementation:** - Using Muuri library for masonry grid layout - Notes have size property: 'small' | 'medium' | 'large' - Layout options include drag settings but not optimized for responsiveness - Grid uses absolute positioning with width: 100% but no column count management **Issues Identified:** 1. **Responsive Layout Issues:** - No defined column counts for different screen sizes - Grid doesn't adjust number of columns when window resizes - Items may overlap or leave gaps - Layout breaks on mobile devices 2. **Drag & Drop Issues:** - Items may not maintain their positions during drag - Visual feedback is minimal - Drag handle only visible on mobile, but desktop dragging may interfere with content interaction - Auto-scroll settings may not be optimal 3. **Note Size Issues:** - Note sizes (small, medium, large) are defined but may not be applied correctly to CSS - No visual distinction between sizes - Size changes during drag may cause layout shifts ### Google Keep Reference Behavior **Google Keep Layout Characteristics:** - Fixed card width (e.g., 240px on desktop, variable on mobile) - Height varies based on content + size setting - Responsive columns: - Mobile (320px-480px): 1 column - Tablet (481px-768px): 2 columns - Desktop (769px-1200px): 3-4 columns - Large Desktop (1201px+): 4-5 columns - Cards have rounded corners, shadow on hover - Smooth animations for drag and resize **Google Keep Drag & Drop:** - Entire card is draggable on desktop - Long press to drag on mobile - Visual feedback: opacity reduction, shadow increase - Placeholder shows drop position - Auto-scroll when dragging near edges - Items reorder smoothly with animation ### Solution Architecture **Responsive Layout Strategy:** Option 1: CSS Grid + Muuri for Drag/Drop ```css .masonry-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; } ``` - Pros: Native CSS responsive behavior - Cons: Muuri may conflict with CSS Grid positioning Option 2: Muuri with Responsive Configuration (RECOMMENDED) ```javascript const getColumns = (width) => { if (width < 640) return 1; if (width < 1024) return 2; if (width < 1280) return 3; return 4; }; ``` - Pros: Muuri handles all positioning and drag/drop - Cons: Requires JavaScript to update on resize **Drag & Drop Improvements:** - Improve visual feedback during drag - Optimize auto-scroll speed - Add transition animations - Ensure mobile touch support **Note Size Implementation:** ```css .note-card[data-size="small"] { min-height: 150px; } .note-card[data-size="medium"] { min-height: 200px; } .note-card[data-size="large"] { min-height: 300px; } ``` ### Implementation Plan #### Step 1: Define Responsive Breakpoints and Dimensions Create a configuration file for layout settings: ```typescript // keep-notes/config/masonry-layout.ts export interface MasonryLayoutConfig { breakpoints: { mobile: number; // < 640px tablet: number; // 640px - 1024px desktop: number; // 1024px - 1280px largeDesktop: number; // > 1280px }; columns: { mobile: number; tablet: number; desktop: number; largeDesktop: number; }; noteSizes: { small: { minHeight: number; width: number }; medium: { minHeight: number; width: number }; large: { minHeight: number; width: number }; }; gap: number; gutter: number; } export const DEFAULT_LAYOUT: MasonryLayoutConfig = { breakpoints: { mobile: 640, tablet: 1024, desktop: 1280, largeDesktop: 1920, }, columns: { mobile: 1, tablet: 2, desktop: 3, largeDesktop: 4, }, noteSizes: { small: { minHeight: 150, width: 240 }, medium: { minHeight: 200, width: 240 }, large: { minHeight: 300, width: 240 }, }, gap: 16, gutter: 16, }; ``` #### Step 2: Update Muuri Configuration Modify `masonry-grid.tsx` to use responsive configuration: ```typescript // Dynamic column calculation based on window width const getLayoutOptions = (containerWidth: number) => { const columns = calculateColumns(containerWidth); const itemWidth = (containerWidth - (columns - 1) * DEFAULT_LAYOUT.gap) / columns; return { dragEnabled: true, dragHandle: isMobile ? '.muuri-drag-handle' : undefined, dragContainer: document.body, dragStartPredicate: { distance: 10, delay: 0, }, dragPlaceholder: { enabled: true, createElement: (item: any) => { const el = item.getElement().cloneNode(true); el.style.opacity = '0.4'; el.style.transform = 'scale(1.05)'; return el; }, }, dragAutoScroll: { targets: [window], speed: (item: any, target: any, intersection: any) => { return intersection * 30; // Faster auto-scroll }, threshold: 50, // Start auto-scroll earlier smoothStop: true, }, layoutDuration: 300, layoutEasing: 'cubic-bezier(0.25, 1, 0.5, 1)', fillGaps: true, horizontal: false, alignRight: false, alignBottom: false, rounding: false, }; }; // Calculate columns based on container width const calculateColumns = (width: number) => { if (width < DEFAULT_LAYOUT.breakpoints.mobile) return DEFAULT_LAYOUT.columns.mobile; if (width < DEFAULT_LAYOUT.breakpoints.tablet) return DEFAULT_LAYOUT.columns.tablet; if (width < DEFAULT_LAYOUT.breakpoints.desktop) return DEFAULT_LAYOUT.columns.desktop; return DEFAULT_LAYOUT.columns.largeDesktop; }; ``` #### Step 3: Apply Note Sizes with CSS Add CSS classes for different note sizes: ```css /* keep-notes/components/masonry-grid.css */ .masonry-item-content .note-card[data-size="small"] { min-height: 150px; } .masonry-item-content .note-card[data-size="medium"] { min-height: 200px; } .masonry-item-content .note-card[data-size="large"] { min-height: 300px; } /* Responsive adjustments */ @media (max-width: 640px) { .masonry-item-content .note-card { width: 100%; } .masonry-item-content .note-card[data-size="small"] { min-height: 120px; } .masonry-item-content .note-card[data-size="medium"] { min-height: 160px; } .masonry-item-content .note-card[data-size="large"] { min-height: 240px; } } /* Drag state improvements */ .masonry-item.muuri-item-dragging .note-card { transform: scale(1.02); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); } .masonry-item.muuri-item-releasing .note-card { transition: transform 0.2s ease-out, box-shadow 0.2s ease-out; } ``` #### Step 4: Add Resize Handler for Responsive Updates Add resize listener to update layout when window size changes: ```typescript useEffect(() => { const handleResize = () => { if (!pinnedMuuri.current || !othersMuuri.current) return; const containerWidth = window.innerWidth - 32; // Subtract padding const columns = calculateColumns(containerWidth); // Update Muuri settings [pinnedMuuri.current, othersMuuri.current].forEach(grid => { if (grid) { grid.refreshItems().layout(); } }); }; const debouncedResize = debounce(handleResize, 150); window.addEventListener('resize', debouncedResize); return () => { window.removeEventListener('resize', debouncedResize); }; }, []); ``` #### Step 5: Update NoteCard to Display Size Attribute Ensure NoteCard component renders with data-size attribute: ```typescript // In NoteCard component ``` #### Step 6: Test on Multiple Devices **Test Matrix:** 1. **Mobile (< 640px)** - 1 column layout - Drag handle visible - Notes stack vertically - Touch interaction works 2. **Tablet (640px - 1024px)** - 2 column layout - Desktop drag behavior - Notes align in columns 3. **Desktop (1024px - 1280px)** - 3 column layout - Smooth drag and drop - Responsive to window resize 4. **Large Desktop (> 1280px)** - 4 column layout - Optimal use of space - No layout issues ### Files to Create - `keep-notes/config/masonry-layout.ts` - Layout configuration - `keep-notes/components/masonry-grid.css` - Masonry-specific styles ### Files to Modify - `keep-notes/components/masonry-grid.tsx` - Update Muuri configuration and add resize handler - `keep-notes/components/note-card.tsx` - Add data-size attribute - `keep-notes/app/globals.css` - Add note size styles if not in separate CSS file ### Testing Checklist **Responsive Behavior:** - [ ] Layout adjusts columns when resizing window - [ ] No items overlap or create gaps - [ ] Mobile shows 1 column - [ ] Tablet shows 2 columns - [ ] Desktop shows 3-4 columns - [ ] Layout matches Google Keep behavior **Drag & Drop Behavior:** - [ ] Notes can be dragged smoothly - [ ] Visual feedback during drag (opacity, shadow) - [ ] Placeholder shows drop position - [ ] Auto-scroll works when dragging near edges - [ ] Order is saved after drop - [ ] Notes maintain their positions - [ ] Works on both desktop and mobile **Note Sizes:** - [ ] Small notes display compactly - [ ] Medium notes display with standard height - [ ] Large notes display with expanded height - [ ] Sizes are maintained during drag - [ ] Sizes persist after drop - [ ] Size changes update layout correctly **Cross-Browser:** - [ ] Chrome: Works correctly - [ ] Firefox: Works correctly - [ ] Safari: Works correctly - [ ] Edge: Works correctly ### Performance Considerations - Debounce resize events to avoid excessive re-layouts - Use requestAnimationFrame for smooth animations - Avoid re-initializing Muuri on resize, use refreshItems() instead - Optimize drag placeholder creation to avoid expensive DOM operations ### Accessibility Considerations - Ensure drag handles are keyboard accessible - Add ARIA attributes for drag state - Provide visual feedback for screen readers - Maintain focus management during drag ### References - **Muuri Documentation:** https://github.com/haltu/muuri - **Google Keep UI Reference:** https://keep.google.com - **CSS Masonry Layout:** https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Masonry_Layout - **Responsive Design Patterns:** https://www.smashingmagazine.com/2018/05/learning-layouts-with-css-grid/ ## Dev Agent Record ### Initial Analysis (2026-01-18) **Problems Identified:** 1. Muuri configuration lacks responsive column management 2. No resize handler to update layout on window resize 3. Note sizes (small, medium, large) are not visually applied via CSS 4. Drag & drop feedback could be improved 5. Mobile drag handle optimization needed **Solution Approach:** - Implement responsive column calculation based on window width - Add resize listener with debounce to update layout - Apply note sizes via CSS data attributes - Improve drag & drop visual feedback - Test thoroughly on multiple devices ### Implementation Progress - [x] Analyze current implementation - [x] Research best practices - [x] Create detailed fix plan - [ ] Implement fixes - [ ] Test and validate ### Agent Model Used claude-sonnet-4.5-20250929 ### Completion Notes List - [x] Analyzed Muuri configuration in masonry-grid.tsx - [x] Reviewed note size handling (small, medium, large) - [x] Identified drag & drop issues - [x] Identified responsive layout issues - [x] Studied Google Keep's masonry layout behavior - [x] Researched Muuri layout options and responsive configuration - [x] Documented optimal settings for responsive masonry grids - [x] Created comprehensive fix plan with step-by-step instructions - [x] Defined responsive breakpoints - [x] Defined note size dimensions - [ ] Fix responsive layout configuration - [ ] Fix drag & drop behavior - [ ] Ensure note sizes are properly applied - [ ] Test on multiple screen sizes ### File List **Files to Create:** - `keep-notes/config/masonry-layout.ts` - `keep-notes/components/masonry-grid.css` **Files to Modify:** - `keep-notes/components/masonry-grid.tsx` - `keep-notes/components/note-card.tsx` - `keep-notes/app/globals.css` (optional, depending on CSS organization)