Skip to content

Commit 7ebe54c

Browse files
RyanHanszRyan Hanszclaudemichelle-hadfield-nava
authored
Add placeholder skeleton cards during resource loading (#172)
Co-authored-by: Ryan Hansz <ryan@Ryans-MacBook-Pro-2.local> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Michelle Hadfield <116575227+MichelleHadfield@users.noreply.github.com>
1 parent 530505f commit 7ebe54c

1 file changed

Lines changed: 41 additions & 2 deletions

File tree

frontend/src/components/ResourcesList.tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,38 @@ type ResourcesListProps = {
109109
isSearching?: boolean;
110110
};
111111

112+
// NOTE: If you change the card layout below, update this placeholder to match
113+
const PlaceholderCard = ({ index }: { index: number }) => (
114+
<Card
115+
className="relative bg-white shadow-sm mb-5 min-w-[16rem] border-t-4 border-t-gray-300 rounded-t-lg"
116+
data-testid={`resource-card-placeholder-${index}`}
117+
aria-hidden="true"
118+
>
119+
{/* Placeholder referral type indicator */}
120+
<span className="inline-flex items-center gap-1.5 bg-transparent ml-4 mt-3 px-2.5 py-1 max-w-[15rem]">
121+
<span className="h-4 w-4 shrink-0 bg-gray-200 rounded animate-pulse" />
122+
<span className="bg-gray-200 rounded animate-pulse w-16 h-4" />
123+
</span>
124+
<CardHeader className="p-3 ml-3">
125+
<CardTitle className="text-xl font-semibold text-gray-900 flex items-center gap-2">
126+
<span className="flex-shrink-0 w-7 h-7 bg-gray-300 text-white rounded-full flex items-center justify-center text-m font-medium animate-pulse">
127+
{index + 1}
128+
</span>
129+
<div className="h-6 bg-gray-200 rounded animate-pulse w-48" />
130+
</CardTitle>
131+
</CardHeader>
132+
<CardContent className="ml-9 mr-4 space-y-2">
133+
<div className="h-4 bg-gray-200 rounded animate-pulse w-64" />
134+
<div className="h-4 bg-gray-200 rounded animate-pulse w-40" />
135+
<div className="h-4 bg-gray-200 rounded animate-pulse w-56" />
136+
<div className="mt-3 space-y-1">
137+
<div className="h-4 bg-gray-200 rounded animate-pulse w-full" />
138+
<div className="h-4 bg-gray-200 rounded animate-pulse w-3/4" />
139+
</div>
140+
</CardContent>
141+
</Card>
142+
);
143+
112144
const ResourcesList = ({
113145
resources,
114146
errorMessage,
@@ -117,7 +149,14 @@ const ResourcesList = ({
117149
}: ResourcesListProps) => {
118150
if (resources.length === 0) {
119151
if (isSearching) {
120-
return <div className="m-3">Searching for resources...</div>;
152+
return (
153+
<div className="mt-2" role="status" aria-label="Loading resources">
154+
<span className="sr-only">Loading resources, please wait...</span>
155+
<PlaceholderCard index={0} />
156+
<PlaceholderCard index={1} />
157+
<PlaceholderCard index={2} />
158+
</div>
159+
);
121160
}
122161
return <div className="m-3">{errorMessage || "No resources found."}</div>;
123162
}
@@ -177,7 +216,7 @@ const ResourcesList = ({
177216
href={normalizeUrl(r.website)}
178217
rel="noopener noreferrer"
179218
target="_blank"
180-
className="text-blue-600 hover:underline"
219+
className="text-blue-600 hover:underline break-all"
181220
prefetch={false}
182221
data-testid={`website-link-${i}`}
183222
>

0 commit comments

Comments
 (0)