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/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 */}