From a14ccf74b89a79b0258cb6f5eda7f19ff0f95f3a Mon Sep 17 00:00:00 2001 From: krusty-agent Date: Tue, 10 Feb 2026 10:02:47 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=A0=20Redesign:=20Home=20Screen=20?= =?UTF-8?q?=E2=86=92=20Modern=20Minimal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Card layout with rounded-2xl corners, subtle shadows, clean whitespace - Floating Action Button (FAB) with rounded-2xl, amber-500 gradient - Warm cream/amber CSS custom properties (design system tokens) - Streamlined ListCard: smaller emoji badge, tighter typography, active:scale - Pill-style quick action chips (Focus, Categories) - List count in header, stone-based neutrals for modern minimal feel - Optimized spacing and touch targets for mobile --- src/components/ListCard.tsx | 75 +++++++------------------- src/index.css | 34 ++++++++++++ src/pages/Home.tsx | 102 +++++++++++++++++++----------------- 3 files changed, 107 insertions(+), 104 deletions(-) diff --git a/src/components/ListCard.tsx b/src/components/ListCard.tsx index da51dc2..13c12ef 100644 --- a/src/components/ListCard.tsx +++ b/src/components/ListCard.tsx @@ -1,8 +1,8 @@ /** - * Card displaying a list summary with improved design. + * Card displaying a list summary — modern minimal design. * - * Shows list name, ownership status, and visual indicators. - * Features dark mode support and card hover effects. + * Clean card with warm amber accents, subtle shadows, rounded corners. + * Optimized for mobile with good touch targets and whitespace. */ import { memo } from "react"; @@ -13,24 +13,20 @@ import { useSettings } from "../hooks/useSettings"; interface ListCardProps { list: Doc<"lists">; currentUserDid: string; - /** Show owner attribution for shared lists */ showOwner?: boolean; } -/** Truncate a DID for display, showing first and last parts */ function truncateDid(did: string): string { if (did.length <= 24) return did; return `${did.slice(0, 12)}...${did.slice(-8)}`; } -/** Get a deterministic emoji based on list name */ function getListEmoji(name: string): string { const emojis = ['📋', '📝', '✅', '📌', '🎯', '📊', '🗂️', '📒', '📓', '🗒️']; const index = name.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) % emojis.length; return emojis[index]; } -/** Format relative time */ function formatRelativeTime(timestamp: number): string { const seconds = Math.floor((Date.now() - timestamp) / 1000); @@ -51,71 +47,38 @@ export const ListCard = memo(function ListCard({ list, currentUserDid, showOwner haptic('light')} - className="group block bg-white dark:bg-gray-800/80 rounded-3xl shadow-sm hover:shadow-xl dark:shadow-gray-900/30 transition-all duration-300 p-6 sm:p-7 border border-gray-100/80 dark:border-gray-700/50 hover:border-amber-300/60 dark:hover:border-amber-600/60 backdrop-blur-sm" + className="group block bg-white dark:bg-stone-800/70 rounded-2xl shadow-sm shadow-stone-200/60 dark:shadow-none hover:shadow-md hover:shadow-stone-200/80 dark:hover:shadow-none transition-all duration-200 p-5 border border-stone-100 dark:border-stone-700/40 hover:border-amber-300/50 dark:hover:border-amber-700/40 active:scale-[0.98]" aria-label={`Open list: ${list.name}`} > -
- {/* Emoji icon - larger, more prominent */} -
+
+ {/* Emoji badge */} +
{emoji}
- {/* List name and badges */} -
-

+ {/* List name */} +
+

{list.name}

{!isOwner && ( - - - - + Shared )}
- {/* Metadata */} -
- {isOwner ? ( - - - - - You - - ) : showOwner ? ( - - - - - {truncateDid(list.ownerDid)} - - ) : ( - - - - - Shared - - )} - - - - - - {formatRelativeTime(list.createdAt)} - -
+ {/* Meta line */} +

+ {showOwner && !isOwner ? truncateDid(list.ownerDid) : formatRelativeTime(list.createdAt)} +

- {/* Arrow indicator - minimal */} -
- - - -
+ {/* Chevron */} + + +
); diff --git a/src/index.css b/src/index.css index 6a01c5a..415ac38 100644 --- a/src/index.css +++ b/src/index.css @@ -9,6 +9,40 @@ --safe-area-bottom: env(safe-area-inset-bottom, 0px); --safe-area-left: env(safe-area-inset-left, 0px); --safe-area-right: env(safe-area-inset-right, 0px); + + /* Warm Cream/Amber Design System - 💩 Brand Colors */ + --cream-50: #fffcf5; + --cream-100: #fef8ed; + --cream-200: #fcefd5; + --cream-300: #fae5bd; + + --amber-400: #fbbf24; + --amber-500: #f59e0b; + --amber-600: #d97706; + --amber-700: #b45309; + --amber-800: #92400e; + --amber-900: #78350f; + + --warm-50: #fafaf9; + --warm-100: #f5f5f4; + --warm-200: #e7e5e4; + --warm-300: #d6d3d1; + --warm-400: #a8a29e; + --warm-500: #78716c; + --warm-600: #57534e; + --warm-700: #44403c; + --warm-800: #292524; + --warm-900: #1c1917; + + --bg-primary: var(--cream-50); + --bg-secondary: var(--cream-100); + --bg-tertiary: var(--cream-200); + --text-primary: var(--warm-900); + --text-secondary: var(--warm-700); + --text-tertiary: var(--warm-600); + --border-color: var(--cream-300); + --accent-primary: var(--amber-600); + --accent-hover: var(--amber-500); } /* Apply to the main layout */ diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 629c5c2..44efd55 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,6 +1,7 @@ /** * Home page showing user's lists. * + * Modern minimal design with card layout, FAB, warm cream/amber palette. * Displays lists grouped by category with collapsible sections. * Includes search, sorting, pull-to-refresh, and improved empty states. */ @@ -55,7 +56,6 @@ export function Home() { // Pull-to-refresh handler const handleRefresh = useCallback(async () => { haptic('medium'); - // Lists auto-refresh via Convex, but we can force a small delay for UX await new Promise(resolve => setTimeout(resolve, 800)); haptic('success'); }, [haptic]); @@ -98,7 +98,6 @@ export function Home() { const processedLists = useMemo(() => { if (!lists) return undefined; - // Filter by search query let filtered = lists; if (searchQuery.trim()) { const query = searchQuery.toLowerCase(); @@ -107,7 +106,6 @@ export function Home() { ); } - // Sort lists return [...filtered].sort((a, b) => { switch (listSort) { case 'name-asc': @@ -123,13 +121,11 @@ export function Home() { }); }, [lists, searchQuery, listSort]); - // Type for grouped lists by category type GroupedLists = { categorized: Map, Doc<"lists">[]>; uncategorized: Doc<"lists">[]; }; - // Split lists into owned and shared, then group each by category const { ownedLists, sharedLists } = useMemo<{ ownedLists: GroupedLists; sharedLists: GroupedLists }>(() => { const isOwnedByUser = (list: Doc<"lists">) => { const ownerDid = list.ownerDid; @@ -186,15 +182,16 @@ export function Home() { }; if (!did && !userLoading) { - return null; // Login page will show instead + return null; } const isLoading = lists === undefined || categoriesLoading; const hasLists = lists && lists.length > 0; const hasFilteredResults = processedLists && processedLists.length > 0; + const totalListCount = lists?.length ?? 0; return ( -
+
{/* Pull-to-refresh indicator */} - {/* Header - Modern Minimal */} -
-

- Your Lists -

-
+ {/* Header */} +
+
+
+

+ Your Lists +

+ {hasLists && ( +

+ {totalListCount} {totalListCount === 1 ? 'list' : 'lists'} +

+ )} +
+
+ + {/* Quick Actions - pill chips */} +
haptic('light')} - className="inline-flex items-center gap-2 text-sm text-amber-700 dark:text-amber-400 px-4 py-2.5 rounded-2xl font-medium bg-amber-50 dark:bg-amber-950/40 hover:bg-amber-100 dark:hover:bg-amber-950/60 border border-amber-200/50 dark:border-amber-800/50 focus:outline-none focus:ring-2 focus:ring-amber-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900 transition-all active:scale-95" + className="inline-flex items-center gap-1.5 text-[13px] text-amber-700 dark:text-amber-400 pl-3 pr-3.5 py-2 rounded-full font-semibold bg-amber-50 dark:bg-amber-950/40 hover:bg-amber-100 dark:hover:bg-amber-900/50 border border-amber-200/60 dark:border-amber-800/50 transition-all active:scale-95" > - 🎯 + 🎯 Focus
@@ -231,7 +239,7 @@ export function Home() { {/* Search and Sort */} {hasLists && ( -
+
)} - {/* Cached data indicator */} + {/* Offline indicator */} {usingCache && ( -
- 📡 - You're offline. Showing cached lists — some info may be outdated. +
+ 📡 + Offline — showing cached lists
)} - {/* Loading state */} + {/* Loading */} {isLoading && } - {/* Empty state - no lists at all */} + {/* Empty state - no lists */} {!isLoading && !hasLists && (
@@ -266,13 +274,12 @@ export function Home() {
)} - {/* Lists - Modern Card Layout */} + {/* Lists */} {!isLoading && hasFilteredResults && did && ( -
- {/* Your Lists section (owned by user) */} +
+ {/* Owned lists */} {(ownedLists.uncategorized.length > 0 || Array.from(ownedLists.categorized.values()).some(l => l.length > 0)) && (
- {/* Categorized owned lists */} {categories.map((category) => { const categoryLists = ownedLists.categorized.get(category._id); if (!categoryLists || categoryLists.length === 0) return null; @@ -283,9 +290,9 @@ export function Home() { name={category.name} listCount={categoryLists.length} > -
+
{categoryLists.map((list, index) => ( -
+
))} @@ -294,15 +301,14 @@ export function Home() { ); })} - {/* Uncategorized owned lists */} {ownedLists.uncategorized.length > 0 && ( -
+
{ownedLists.uncategorized.map((list, index) => ( -
+
))} @@ -312,15 +318,16 @@ export function Home() {
)} - {/* Shared with me section */} + {/* Shared lists */} {(sharedLists.uncategorized.length > 0 || Array.from(sharedLists.categorized.values()).some(l => l.length > 0)) && ( -
-

- 🤝 - Shared with me -

+
+
+ 🤝 +

+ Shared with me +

+
- {/* Categorized shared lists */} {categories.map((category) => { const categoryLists = sharedLists.categorized.get(category._id); if (!categoryLists || categoryLists.length === 0) return null; @@ -331,9 +338,9 @@ export function Home() { name={category.name} listCount={categoryLists.length} > -
+
{categoryLists.map((list, index) => ( -
+
))} @@ -342,15 +349,14 @@ export function Home() { ); })} - {/* Uncategorized shared lists */} {sharedLists.uncategorized.length > 0 && ( -
+
{sharedLists.uncategorized.map((list, index) => ( -
+
))} @@ -362,14 +368,14 @@ export function Home() {
)} - {/* Floating Action Button (FAB) - Modern Material Design 3 */} + {/* FAB */}