From c4f462c6022d3c6798e71708e13e775c0b336f04 Mon Sep 17 00:00:00 2001 From: os-zhuang Date: Thu, 11 Jun 2026 21:32:48 +0500 Subject: [PATCH] feat(ux): depth + entrance on shared EmptyState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give every empty/error/idle screen more personality: the icon now sits in a layered badge (a faint concentric halo behind a tinted tile) instead of a flat grey square, switched to the brand tint, and the group eases in — the badge springs (ZoomIn), the title/description/action rise with a short stagger. The root stays a plain View so flex-1 centering is never affected by the animation; only the inner pieces animate. Co-Authored-By: Claude Opus 4.8 (1M context) --- components/ui/EmptyState.tsx | 78 ++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/components/ui/EmptyState.tsx b/components/ui/EmptyState.tsx index 8eb2e82..7efbd3f 100644 --- a/components/ui/EmptyState.tsx +++ b/components/ui/EmptyState.tsx @@ -1,5 +1,6 @@ import React from "react"; -import { View, Text } from "react-native"; +import { View } from "react-native"; +import Animated, { FadeInDown, ZoomIn } from "react-native-reanimated"; import type { LucideIcon } from "lucide-react-native"; import { Button } from "~/components/ui/Button"; @@ -19,7 +20,10 @@ export interface EmptyStateProps { /** * A centred icon-badge + title + body used for empty, error, and idle states - * across the app. Keeps every "nothing here" screen visually consistent. + * across the app. The badge sits inside a soft concentric halo for depth, and + * the whole group eases in — the badge springs, the copy rises with a short + * stagger — so "nothing here" feels considered rather than blank. Keeps every + * empty/error screen visually consistent. */ export function EmptyState({ icon: Icon, @@ -33,31 +37,61 @@ export function EmptyState({ }: EmptyStateProps) { const isError = variant === "error"; return ( - - + {/* Layered badge: a faint outer halo behind a solid inner tile gives the + icon depth instead of floating on a flat square. */} + - - - {title} + + + + + + + + {title} + + {description ? ( - + {description} - + ) : null} + {actionLabel && onAction ? ( - + + + ) : null} );