Skip to content

Commit d7b0c0f

Browse files
Merge pull request #99 from NithinU2802/feature/activity-page-ui-update
To Enhance Activities Page UI Consistency and Responsiveness
2 parents cf4b858 + ba3d771 commit d7b0c0f

File tree

4 files changed

+404
-24
lines changed

4 files changed

+404
-24
lines changed
Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,50 @@
1-
import {Link} from "react-router-dom";
1+
import React, { useState } from "react";
2+
import { Link } from "react-router-dom";
23
import "../../styles/components/activities/ActivityCard.css"
34

4-
export const ActivityCard = ({activity}) => {
5+
export const ActivityCard = ({ activity }) => {
6+
const [imageLoaded, setImageLoaded] = useState(false);
7+
const [imageError, setImageError] = useState(false);
8+
9+
const handleImageLoad = () => {
10+
setImageLoaded(true);
11+
};
12+
13+
const handleImageError = () => {
14+
setImageError(true);
15+
setImageLoaded(true);
16+
};
17+
18+
// Default icon for activities without an image
19+
const defaultIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMTIiIGZpbGw9IiNlMmU4ZjAiLz4KPHN2ZyB4PSIyNCIgeT0iMjQiIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiM2Mzc0OGIiIHN0cm9rZS13aWR0aD0iMiI+CjxwYXRoIGQ9Im0xMiAzLTEuOTEyIDUuODEzYTIgMiAwIDAgMS0xLjI3NSAxLjI3NUwzIDEybDUuODEzIDEuOTEyYTIgMiAwIDAgMSAxLjI3NSAxLjI3NUwxMiAyMWwxLjkxMi01LjgxM2EyIDIgMCAwIDEgMS4yNzUtMS4yNzVMMjEgMTJsLTUuODEzLTEuOTEyYTIgMiAwIDAgMS0xLjI3NS0xLjI3NUwxMiAzeiIvPgo8L3N2Zz4KPC9zdmc+";
20+
521
return (
6-
<Link className="activity-card-root" to={`/activities/${activity.urlTerm}`}>
7-
<img src={activity.icon} alt={activity.title} />
8-
<h1 className="activity-card-title">{activity.title}</h1>
9-
<div className="activity-card-desc">{activity.description}</div>
22+
<Link
23+
className="activity-card-root"
24+
to={`/activities/${activity.urlTerm}`}
25+
role="button"
26+
tabIndex={0}
27+
aria-label={`Navigate to ${activity.title} activity: ${activity.description}`}
28+
>
29+
<div className="activity-card-image-container">
30+
{!imageLoaded && !imageError && (
31+
<div className="image-placeholder">
32+
<div className="loading-spinner"></div>
33+
</div>
34+
)}
35+
<img
36+
src={imageError ? defaultIcon : activity.icon}
37+
alt={`${activity.title} icon`}
38+
onLoad={handleImageLoad}
39+
onError={handleImageError}
40+
style={{
41+
opacity: imageLoaded ? 1 : 0,
42+
transition: 'opacity 0.3s ease'
43+
}}
44+
/>
45+
</div>
46+
<h2 className="activity-card-title">{activity.title}</h2>
47+
<p className="activity-card-desc">{activity.description}</p>
1048
</Link>
1149
)
12-
}
50+
}

src/pages/Activities.js

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,58 @@
1+
import React, { useState, useEffect } from "react";
12
import "../styles/pages/Activities.css"
23
import {activities} from "../data/content";
34
import {ActivityCard} from "../components/activities/ActivityCard";
45

56
export const Activities = () => {
7+
const [isLoading, setIsLoading] = useState(true);
8+
9+
useEffect(() => {
10+
// Simulate loading for smooth transition
11+
const timer = setTimeout(() => {
12+
setIsLoading(false);
13+
}, 300);
14+
15+
return () => clearTimeout(timer);
16+
}, []);
17+
618
return (
719
<div className="activities-root">
820
<h1 className="activities-title">Activities</h1>
921
<div className="activities-content">
10-
{
11-
activities.map(activity => {
12-
return (
13-
<ActivityCard activity={activity} />
14-
)
15-
})
16-
}
22+
{isLoading ? (
23+
// Loading skeleton
24+
Array.from({ length: 6 }).map((_, index) => (
25+
<div key={index} className="activity-card-root loading">
26+
<div style={{
27+
width: '80px',
28+
height: '80px',
29+
backgroundColor: '#e2e8f0',
30+
borderRadius: '12px',
31+
marginBottom: '20px'
32+
}}></div>
33+
<div style={{
34+
width: '80%',
35+
height: '20px',
36+
backgroundColor: '#e2e8f0',
37+
borderRadius: '4px',
38+
marginBottom: '12px'
39+
}}></div>
40+
<div style={{
41+
width: '100%',
42+
height: '40px',
43+
backgroundColor: '#e2e8f0',
44+
borderRadius: '4px'
45+
}}></div>
46+
</div>
47+
))
48+
) : (
49+
activities.map((activity, index) => (
50+
<ActivityCard
51+
key={activity.urlTerm || index}
52+
activity={activity}
53+
/>
54+
))
55+
)}
1756
</div>
1857
</div>
1958
)
Lines changed: 233 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,246 @@
11
.activity-card-root {
2-
padding: 10px;
3-
margin: 10px;
2+
padding: 25px 20px;
3+
margin: 0;
44
display: flex;
55
flex-direction: column;
66
align-items: center;
7-
box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
8-
transition-duration: 300ms;
7+
background: #ffffff;
8+
border-radius: 20px;
9+
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
10+
backdrop-filter: blur(4px);
11+
border: 1px solid rgba(255, 255, 255, 0.18);
12+
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
913
text-decoration: none;
14+
color: inherit;
15+
position: relative;
16+
overflow: hidden;
17+
min-height: 280px;
18+
justify-content: space-between;
1019
}
1120

21+
.activity-card-root::before {
22+
content: '';
23+
position: absolute;
24+
top: 0;
25+
left: 0;
26+
right: 0;
27+
height: 4px;
28+
background: linear-gradient(90deg, #26b4ec, #032d7a, #26b4ec);
29+
background-size: 200% 100%;
30+
animation: shimmer 3s ease-in-out infinite;
31+
}
32+
33+
@keyframes shimmer {
34+
0%, 100% { background-position: 200% 0; }
35+
50% { background-position: -200% 0; }
36+
}
1237

1338
.activity-card-root:hover {
14-
box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
15-
background: lightgray;
39+
transform: translateY(-10px) scale(1.02);
40+
box-shadow: 0 20px 60px rgba(31, 38, 135, 0.25);
41+
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
42+
}
43+
44+
.activity-card-root:hover::before {
45+
animation-duration: 1s;
46+
}
47+
48+
.activity-card-image-container {
49+
position: relative;
50+
width: 80px;
51+
height: 80px;
52+
margin-bottom: 20px;
1653
}
1754

1855
.activity-card-root img {
19-
height: 100px;
56+
height: 80px;
57+
width: 80px;
58+
border-radius: 12px;
59+
transition: all 0.3s ease;
60+
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.1));
61+
position: absolute;
62+
top: 0;
63+
left: 0;
64+
}
65+
66+
.activity-card-root:hover img {
67+
transform: scale(1.1);
68+
filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.2));
69+
}
70+
71+
.image-placeholder {
72+
position: absolute;
73+
top: 0;
74+
left: 0;
2075
width: 80px;
21-
}
76+
height: 80px;
77+
background: #f1f5f9;
78+
border-radius: 12px;
79+
display: flex;
80+
align-items: center;
81+
justify-content: center;
82+
}
83+
84+
.loading-spinner {
85+
width: 24px;
86+
height: 24px;
87+
border: 3px solid #e2e8f0;
88+
border-top: 3px solid #26b4ec;
89+
border-radius: 50%;
90+
animation: spin 1s linear infinite;
91+
}
92+
93+
@keyframes spin {
94+
0% { transform: rotate(0deg); }
95+
100% { transform: rotate(360deg); }
96+
}
97+
98+
.activity-card-title {
99+
font-size: 1.4rem;
100+
font-weight: 600;
101+
color: #032d7a;
102+
margin: 0 0 12px 0;
103+
text-align: center;
104+
line-height: 1.3;
105+
transition: color 0.3s ease;
106+
}
107+
108+
.activity-card-root:hover .activity-card-title {
109+
color: #26b4ec;
110+
}
111+
112+
.activity-card-desc {
113+
font-size: 0.95rem;
114+
color: #64748b;
115+
text-align: center;
116+
line-height: 1.6;
117+
margin: 0;
118+
font-weight: 400;
119+
flex-grow: 1;
120+
display: flex;
121+
align-items: center;
122+
transition: color 0.3s ease;
123+
}
124+
125+
.activity-card-root:hover .activity-card-desc {
126+
color: #475569;
127+
}
128+
129+
/* Loading state */
130+
.activity-card-root.loading {
131+
pointer-events: none;
132+
opacity: 0.7;
133+
}
134+
135+
/* Responsive Design */
136+
@media screen and (max-width: 768px) {
137+
.activity-card-root {
138+
padding: 20px 15px;
139+
min-height: 250px;
140+
border-radius: 16px;
141+
}
142+
143+
.activity-card-image-container {
144+
width: 70px;
145+
height: 70px;
146+
margin-bottom: 15px;
147+
}
148+
149+
.activity-card-root img,
150+
.image-placeholder {
151+
height: 70px;
152+
width: 70px;
153+
}
154+
155+
.activity-card-title {
156+
font-size: 1.2rem;
157+
margin-bottom: 10px;
158+
}
159+
160+
.activity-card-desc {
161+
font-size: 0.9rem;
162+
}
163+
}
164+
165+
@media screen and (max-width: 480px) {
166+
.activity-card-root {
167+
padding: 18px 12px;
168+
min-height: 220px;
169+
border-radius: 12px;
170+
}
171+
172+
.activity-card-image-container {
173+
width: 60px;
174+
height: 60px;
175+
margin-bottom: 12px;
176+
}
177+
178+
.activity-card-root img,
179+
.image-placeholder {
180+
height: 60px;
181+
width: 60px;
182+
}
183+
184+
.activity-card-title {
185+
font-size: 1.1rem;
186+
margin-bottom: 8px;
187+
}
188+
189+
.activity-card-desc {
190+
font-size: 0.85rem;
191+
line-height: 1.5;
192+
}
193+
194+
.activity-card-root:hover {
195+
transform: translateY(-5px) scale(1.01);
196+
}
197+
}
198+
199+
/* Focus states for accessibility */
200+
.activity-card-root:focus {
201+
outline: 3px solid #26b4ec;
202+
outline-offset: 2px;
203+
}
204+
205+
.activity-card-root:focus:not(:focus-visible) {
206+
outline: none;
207+
}
208+
209+
/* High contrast mode support */
210+
@media (prefers-contrast: high) {
211+
.activity-card-root {
212+
border: 2px solid #032d7a;
213+
background: #ffffff;
214+
}
215+
216+
.activity-card-title {
217+
color: #000000;
218+
}
219+
220+
.activity-card-desc {
221+
color: #333333;
222+
}
223+
}
224+
225+
/* Reduced motion support */
226+
@media (prefers-reduced-motion: reduce) {
227+
.activity-card-root {
228+
transition: none;
229+
}
230+
231+
.activity-card-root img {
232+
transition: none;
233+
}
234+
235+
.activity-card-root::before {
236+
animation: none;
237+
}
238+
239+
.loading-spinner {
240+
animation: none;
241+
}
242+
243+
.activity-card-root:hover {
244+
transform: none;
245+
}
246+
}

0 commit comments

Comments
 (0)