Skip to content

Commit ad2e44b

Browse files
committed
feat(connections): Enhance connections page with improved accessibility and UX
- Add screen reader announcements for tab changes - Implement responsive tab design with mobile-friendly layouts - Add keyboard navigation support for connection stats cards - Introduce new CSS animations for smoother interactions - Enhance focus and hover states for better user engagement - Improve tab content transitions with fade-in animation - Add count animation for connection statistics - Implement touch-friendly button sizing for mobile devices Improves overall user experience and accessibility for the connections page, making navigation more intuitive and interactive across different devices.
1 parent 2c4a149 commit ad2e44b

File tree

4 files changed

+126
-27
lines changed

4 files changed

+126
-27
lines changed

app/globals.css

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,3 +473,66 @@ Accessibility: Enhanced focus indicators for keyboard navigation */
473473
.animate-shimmer {
474474
animation: shimmer 2s infinite;
475475
}
476+
477+
/* Tab content animations */
478+
@keyframes fadeIn {
479+
from {
480+
opacity: 0;
481+
transform: translateY(10px);
482+
}
483+
to {
484+
opacity: 1;
485+
transform: translateY(0);
486+
}
487+
}
488+
489+
.animate-fadeIn {
490+
animation: fadeIn 0.3s ease-out;
491+
}
492+
493+
/* Count animation */
494+
@keyframes countUp {
495+
from {
496+
transform: scale(1.2);
497+
opacity: 0;
498+
}
499+
to {
500+
transform: scale(1);
501+
opacity: 1;
502+
}
503+
}
504+
505+
.animate-countUp {
506+
animation: countUp 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
507+
}
508+
509+
/* Button press animation */
510+
@keyframes buttonPress {
511+
0% {
512+
transform: scale(1);
513+
}
514+
50% {
515+
transform: scale(0.95);
516+
}
517+
100% {
518+
transform: scale(1);
519+
}
520+
}
521+
522+
.animate-buttonPress {
523+
animation: buttonPress 0.2s ease-in-out;
524+
}
525+
526+
/* Focus visible styles for keyboard navigation */
527+
.focus-visible:focus {
528+
outline: 2px solid hsl(var(--ring));
529+
outline-offset: 2px;
530+
}
531+
532+
/* Touch-friendly button sizes */
533+
@media (max-width: 640px) {
534+
button, a {
535+
min-height: 44px;
536+
min-width: 44px;
537+
}
538+
}

app/protected/connections/page.tsx

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,48 +12,63 @@ import { ConnectionStats } from '@/components/connections/ConnectionStats'
1212
export default function ConnectionsPage() {
1313
const [searchQuery, setSearchQuery] = useState('')
1414
const [activeTab, setActiveTab] = useState('following')
15+
const [announcement, setAnnouncement] = useState('')
16+
17+
const handleTabChange = (tab: string) => {
18+
setActiveTab(tab)
19+
const tabNames: Record<string, string> = {
20+
following: 'Following',
21+
followers: 'Followers',
22+
search: 'Search'
23+
}
24+
setAnnouncement(`Switched to ${tabNames[tab]} tab`)
25+
}
1526

1627
return (
1728
<div className="flex flex-col h-[calc(100vh-4rem)] bg-black">
29+
{/* Screen reader announcements */}
30+
<div role="status" aria-live="polite" aria-atomic="true" className="sr-only">
31+
{announcement}
32+
</div>
1833
{/* Header */}
1934
<div className="border-b p-4">
2035
<div className="max-w-7xl mx-auto">
2136
<div className="flex items-center gap-3 mb-4">
2237
<Users className="h-6 w-6 text-primary" />
2338
<h1 className="text-xl md:text-2xl font-bold">Connections</h1>
2439
</div>
25-
<ConnectionStats onTabChange={setActiveTab} />
40+
<ConnectionStats onTabChange={handleTabChange} />
2641
</div>
2742
</div>
2843

2944
{/* Main Content */}
3045
<div className="flex-1 overflow-hidden">
3146
<div className="max-w-7xl mx-auto h-full flex flex-col p-4">
32-
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col">
33-
<TabsList className="grid w-full grid-cols-3 mb-4">
34-
<TabsTrigger value="following" className="gap-2">
47+
<Tabs value={activeTab} onValueChange={handleTabChange} className="flex-1 flex flex-col">
48+
<TabsList className="grid w-full grid-cols-3 mb-4 h-auto">
49+
<TabsTrigger value="following" className="gap-1 sm:gap-2 flex-col sm:flex-row py-2 sm:py-1.5">
3550
<UserPlus className="h-4 w-4" />
36-
<span>Following</span>
51+
<span className="text-xs sm:text-sm">Following</span>
3752
</TabsTrigger>
38-
<TabsTrigger value="followers" className="gap-2">
53+
<TabsTrigger value="followers" className="gap-1 sm:gap-2 flex-col sm:flex-row py-2 sm:py-1.5">
3954
<Users className="h-4 w-4" />
40-
<span>Followers</span>
55+
<span className="text-xs sm:text-sm">Followers</span>
4156
</TabsTrigger>
42-
<TabsTrigger value="search" className="gap-2">
57+
<TabsTrigger value="search" className="gap-1 sm:gap-2 flex-col sm:flex-row py-2 sm:py-1.5">
4358
<Search className="h-4 w-4" />
44-
<span>Search</span>
59+
<span className="text-xs sm:text-sm">Search</span>
4560
</TabsTrigger>
4661
</TabsList>
4762

48-
<TabsContent value="following" className="flex-1 overflow-y-auto space-y-3">
63+
<TabsContent value="following" className="flex-1 overflow-y-auto space-y-3 animate-fadeIn">
4964
<FollowingList />
5065
</TabsContent>
5166

52-
<TabsContent value="followers" className="flex-1 overflow-y-auto space-y-3">
67+
<TabsContent value="followers" className="flex-1 overflow-y-auto space-y-3 animate-fadeIn">
5368
<FollowersList />
5469
</TabsContent>
5570

56-
<TabsContent value="search" className="flex-1 overflow-y-auto space-y-3">
71+
<TabsContent value="search" className="flex-1 overflow-y-auto space-y-3 animate-fadeIn">
5772
<div className="relative mb-4">
5873
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none" />
5974
<Input

components/connections/ConnectionStats.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,42 @@ export function ConnectionStats({ onTabChange }: ConnectionStatsProps) {
5555
return (
5656
<div className="grid grid-cols-2 gap-4">
5757
<Card
58-
className="p-4 cursor-pointer transition-all duration-300 hover:scale-[1.02] hover:shadow-lg hover:border-primary/50 group"
58+
className="p-4 cursor-pointer transition-all duration-300 hover:scale-[1.02] hover:shadow-lg hover:border-primary/50 group focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
5959
onClick={() => onTabChange?.('following')}
60+
role="button"
61+
tabIndex={0}
62+
onKeyDown={(e) => {
63+
if (e.key === 'Enter' || e.key === ' ') {
64+
e.preventDefault()
65+
onTabChange?.('following')
66+
}
67+
}}
68+
aria-label={`View following list. You are following ${stats.following} users`}
6069
>
6170
<div className="flex items-center gap-2 text-muted-foreground mb-1 group-hover:text-primary transition-colors">
62-
<UserPlus className="h-4 w-4" />
71+
<UserPlus className="h-4 w-4" aria-hidden="true" />
6372
<span className="text-sm font-medium">Following</span>
6473
</div>
65-
<p className="text-2xl font-bold group-hover:text-primary transition-colors">{stats.following}</p>
74+
<p className="text-2xl font-bold group-hover:text-primary transition-colors animate-countUp">{stats.following}</p>
6675
</Card>
6776
<Card
68-
className="p-4 cursor-pointer transition-all duration-300 hover:scale-[1.02] hover:shadow-lg hover:border-primary/50 group"
77+
className="p-4 cursor-pointer transition-all duration-300 hover:scale-[1.02] hover:shadow-lg hover:border-primary/50 group focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2"
6978
onClick={() => onTabChange?.('followers')}
79+
role="button"
80+
tabIndex={0}
81+
onKeyDown={(e) => {
82+
if (e.key === 'Enter' || e.key === ' ') {
83+
e.preventDefault()
84+
onTabChange?.('followers')
85+
}
86+
}}
87+
aria-label={`View followers list. You have ${stats.followers} followers`}
7088
>
7189
<div className="flex items-center gap-2 text-muted-foreground mb-1 group-hover:text-primary transition-colors">
72-
<Users className="h-4 w-4" />
90+
<Users className="h-4 w-4" aria-hidden="true" />
7391
<span className="text-sm font-medium">Followers</span>
7492
</div>
75-
<p className="text-2xl font-bold group-hover:text-primary transition-colors">{stats.followers}</p>
93+
<p className="text-2xl font-bold group-hover:text-primary transition-colors animate-countUp">{stats.followers}</p>
7694
</Card>
7795
</div>
7896
)

components/connections/UserCard.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,10 @@ export function UserCard({ user, connectionStatus, onConnectionChange, showMessa
155155
onClick={handleViewProfile}
156156
size="sm"
157157
variant="ghost"
158-
className="gap-1"
159-
title="View Profile"
158+
className="gap-1 active:animate-buttonPress"
159+
aria-label={`View ${name}'s profile`}
160160
>
161-
<Eye className="h-3 w-3" />
161+
<Eye className="h-3 w-3" aria-hidden="true" />
162162
<span className="hidden md:inline">View</span>
163163
</Button>
164164

@@ -168,9 +168,10 @@ export function UserCard({ user, connectionStatus, onConnectionChange, showMessa
168168
disabled={loading}
169169
size="sm"
170170
variant="outline"
171-
className="gap-1"
171+
className="gap-1 active:animate-buttonPress"
172+
aria-label={`Send message to ${name}`}
172173
>
173-
<MessageCircle className="h-3 w-3" />
174+
<MessageCircle className="h-3 w-3" aria-hidden="true" />
174175
<span className="hidden sm:inline">Message</span>
175176
</Button>
176177
)}
@@ -181,19 +182,21 @@ export function UserCard({ user, connectionStatus, onConnectionChange, showMessa
181182
disabled={loading}
182183
size="sm"
183184
variant="outline"
184-
className="gap-1"
185+
className="gap-1 active:animate-buttonPress"
186+
aria-label={`Unfollow ${name}`}
185187
>
186-
<UserMinus className="h-3 w-3" />
188+
<UserMinus className="h-3 w-3" aria-hidden="true" />
187189
<span className="hidden sm:inline">Unfollow</span>
188190
</Button>
189191
) : (
190192
<Button
191193
onClick={handleFollow}
192194
disabled={loading}
193195
size="sm"
194-
className="gap-1"
196+
className="gap-1 active:animate-buttonPress"
197+
aria-label={`Follow ${name}`}
195198
>
196-
<UserPlus className="h-3 w-3" />
199+
<UserPlus className="h-3 w-3" aria-hidden="true" />
197200
<span className="hidden sm:inline">Follow</span>
198201
</Button>
199202
)}

0 commit comments

Comments
 (0)