Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 33 additions & 64 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 9 additions & 7 deletions src/components/Banner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ export function Banner() {
<p className="mt-4 text-lg tracking-tighter text-white dark:text-black font-medium font-mono">
Learn how to apply for an opportunity to work on open-source projects and gain real-world experience through Google Summer of Code.
</p>
<div className="mt-5">
<Link href="/apply" legacyBehavior>
<a className="group relative rounded-lg inline-flex items-center overflow-hidden bg-white dark:bg-black px-8 py-3 text-black dark:text-white focus:outline-none font-mono font-semibold">
Apply to GSoC with AOSSIE
</a>
</Link>
</div>
<div className="mt-5">
<Link href="/apply" legacyBehavior>
<a className="group relative rounded-lg inline-flex items-center overflow-hidden bg-white dark:bg-black px-8 py-3 text-black dark:text-white focus:outline-none font-mono font-semibold">
Apply to GSoC with AOSSIE
</a>
</Link>

</div>

</div>
</ContainerPattern>
</div>
Expand Down
11 changes: 8 additions & 3 deletions src/components/Card.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ function ChevronRightIcon(props) {
export function Card({ as: Component = 'div', className, children }) {
return (
<Component
className={clsx(className, 'group relative flex flex-col items-start')}
className={clsx(
className,
'group relative flex flex-col items-start rounded-2xl border border-zinc-200/60 bg-white p-4 transition-all duration-300 ease-out hover:-translate-y-1 hover:shadow-xl hover:shadow-zinc-200/70 dark:border-zinc-800 dark:bg-zinc-900 dark:hover:shadow-zinc-900/60'
)}
>
{children}
</Component>
)
}



Card.Link = function CardLink({ children, ...props }) {
return (
<>
Expand Down Expand Up @@ -56,10 +61,10 @@ Card.Cta = function CardCta({ children, href}) {
<Link href={href}>
<div
aria-hidden="true"
className="relative font-mono z-10 mt-4 flex items-center text-sm font-semibold text-[#00843D] dark:text-yellow-400"
className="relative font-mono z-10 mt-4 flex items-center text-sm font-semibold text-[#00843D] dark:text-yellow-400 transition-colors group-hover:text-[#006b32]"
>
{children}
<ChevronRightIcon className="ml-1 h-4 w-4 stroke-current" />
<ChevronRightIcon className="ml-1 h-4 w-4 stroke-current transition-transform duration-300 group-hover:translate-x-1" />
</div>
</Link>

Expand Down
5 changes: 5 additions & 0 deletions src/components/skeletonCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function SkeletonCard() {
return (
<div className="skeleton-card" aria-busy="true" role="status" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

role="status" without accessible text is silent to screen readers.

The div has no text content, so assistive technologies will announce nothing for this live region. Add a visually-hidden label so users know content is loading.

Also, aria-busy="true" is already set on the parent grid container in index.jsx, making it redundant here on each individual card.

♻️ Suggested fix
- <div className="skeleton-card" aria-busy="true" role="status" />
+ <div className="skeleton-card" role="status">
+   <span className="sr-only">Loading project…</span>
+ </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="skeleton-card" aria-busy="true" role="status" />
<div className="skeleton-card" role="status">
<span className="sr-only">Loading project…</span>
</div>
🤖 Prompt for AI Agents
In `@src/components/skeletonCard.jsx` at line 3, Remove the redundant aria-busy
from the skeleton card and add an accessible, visually-hidden status message so
screen readers hear that content is loading: in src/components/skeletonCard.jsx
update the <div className="skeleton-card" role="status"> to include a hidden
text node (e.g., a span with a "sr-only" or visually-hidden class) such as
"Loading content" and remove the aria-busy attribute (the parent grid in
index.jsx already provides aria-busy="true"), ensuring the role="status" has
accessible text for assistive technologies.

);
}
51 changes: 37 additions & 14 deletions src/pages/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,23 @@ import { CardEffect } from '@/components/CardEffect'
import { Banner } from '@/components/Banner'
import projects from '@/helper/projects'
import Journey from '@/components/Journey'
import SkeletonCard from '@/components/skeletonCard'

export default function Home() {
const [randomProjects, setRandomProjects] = useState(projects)
const [randomProjects, setRandomProjects] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
const shuffled = [...projects].sort(() => 0.5 - Math.random()).slice(0, 3)
setRandomProjects(shuffled)
}, [])

useEffect(() => {
const timer = setTimeout(() => {
setLoading(false)
}, 800)

return () => clearTimeout(timer)
}, [])
Comment on lines +29 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Artificial 800ms delay when data is already synchronously available.

The projects data is statically imported (Line 17) — it's available immediately. The 800ms setTimeout creates a fake loading state that adds latency on every page visit for no functional benefit. The PR notes this is "temporary and replaceable by real async data later," but until then it actively degrades perceived performance — the opposite of the stated goal.

Consider tying the loading state to actual data readiness instead:

♻️ Suggested simplification
- const [randomProjects, setRandomProjects] = useState([])
- const [loading, setLoading] = useState(true)
- useEffect(() => {
-   const shuffled = [...projects].sort(() => 0.5 - Math.random()).slice(0, 3)
-   setRandomProjects(shuffled)
- }, [])
-
- useEffect(() => {
-   const timer = setTimeout(() => {
-     setLoading(false)
-   }, 800)
-
-   return () => clearTimeout(timer)
- }, [])
+ const [randomProjects, setRandomProjects] = useState([])
+ const loading = randomProjects.length === 0
+
+ useEffect(() => {
+   const shuffled = [...projects].sort(() => 0.5 - Math.random()).slice(0, 3)
+   setRandomProjects(shuffled)
+ }, [])

This shows skeletons only for the single render cycle before the effect fires, matching the actual data lifecycle.

🤖 Prompt for AI Agents
In `@src/pages/index.jsx` around lines 29 - 35, The artificial 800ms delay in the
useEffect (the setTimeout/clearTimeout timer that calls setLoading(false))
creates unnecessary latency even though the statically imported projects data is
immediately available; remove the timeout and instead derive loading from actual
data readiness (e.g., initialize loading to false or
setLoading(Boolean(!projects.length)) and remove the timer code in the
useEffect), or if you only want a transient skeleton for the first render, drop
the setTimeout and let the component render once with loading true then
immediately set loading false synchronously based on the presence of the
imported projects; update or remove the useEffect, timer, and clearTimeout
references accordingly (functions/identifiers: useEffect, setLoading, timer,
clearTimeout, projects).


useEffect(() => {
setRandomProjects(projects.sort(() => 0.5 - Math.random()).slice(0, 3))
Expand Down Expand Up @@ -171,19 +185,28 @@ export default function Home() {
</p>
</div>
<div className="mt-10 flex flex-col items-center gap-6 sm:flex-row sm:justify-evenly sm:gap-0">
<Container.Inner>
<div className="grid grid-cols-1 gap-x-12 gap-y-16 sm:grid-cols-2 lg:grid-cols-3">
{randomProjects.map((project) => (
<span key={project.name}>
<CardEffect
heading={project.name}
logo={project.logo}
content={project.description}
/>
</span>
))}
</div>
</Container.Inner>
<Container.Inner>
<div
className="grid grid-cols-1 gap-x-12 gap-y-16 sm:grid-cols-2 lg:grid-cols-3"
aria-busy={loading}
aria-live="polite"
>
{loading
? Array.from({ length: 3 }).map((_, i) => (
<SkeletonCard key={i} />
))
: randomProjects.map((project) => (
<span key={project.name}>
<CardEffect
heading={project.name}
logo={project.logo}
content={project.description}
/>
</span>
))}
</div>
</Container.Inner>

</div>
<div className="mt-12 text-center">
<Link
Expand Down
47 changes: 30 additions & 17 deletions src/pages/projects.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,26 @@ const Cards = () => {
<Grid container spacing={4} sx={{ paddingTop: '40px', justifyContent: 'center' }}>
{projects.map((project, index) => (
<Grid item xs={12} sm={6} md={4} key={index}>
<MuiCard
className='dark:bg-[#2A2A2A] dark:border-white'
sx={{
height: 400,
borderRadius: 2,
border: '1px solid',
borderColor: '#3c982c',
boxShadow: '0px 4px 4px #00000040',
backdropFilter: 'blur(4px) brightness(100%)',
display: 'flex',
flexDirection: 'column',
}}
>
<MuiCard
className='dark:bg-[#2A2A2A] dark:border-white'
sx={{
height: 400,
borderRadius: 2,
border: '1px solid',
borderColor: '#3c982c',
boxShadow: '0px 4px 4px #00000040',
backdropFilter: 'blur(4px) brightness(100%)',
display: 'flex',
flexDirection: 'column',
transition: 'all 0.3s ease',
cursor: 'pointer',
'&:hover': {
transform: 'translateY(-6px)',
boxShadow: '1px 2px 1px 1px rgba(255,255,0,255)',
},
Comment on lines +48 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Invalid rgba alpha value — use 1 instead of 255.

rgba(255,255,0,255) uses 255 for the alpha channel, but CSS rgba() expects a value between 0 and 1 (or 0%100%). Browsers silently clamp this to 1, so it works today, but it's technically invalid and confusing to future readers.

🐛 Fix
-      boxShadow: '1px 2px 1px 1px rgba(255,255,0,255)',
+      boxShadow: '1px 2px 1px 1px rgba(255, 255, 0, 1)',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'&:hover': {
transform: 'translateY(-6px)',
boxShadow: '1px 2px 1px 1px rgba(255,255,0,255)',
},
'&:hover': {
transform: 'translateY(-6px)',
boxShadow: '1px 2px 1px 1px rgba(255, 255, 0, 1)',
},
🤖 Prompt for AI Agents
In `@src/pages/projects.jsx` around lines 48 - 51, The boxShadow style inside the
hover style object in src/pages/projects.jsx uses an invalid alpha value
(rgba(255,255,0,255)); update the boxShadow value to use a valid alpha between 0
and 1 (for example 'rgba(255,255,0,1)') or an equivalent color string; locate
the '&:hover' style block that contains transform and boxShadow and replace the
alpha 255 with 1 to fix the CSS.

}}
>
Comment on lines +35 to +53
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

cursor: 'pointer' on the card implies the entire card is clickable, but only the inner link is.

The pointer cursor appears over the entire card surface, yet clicking anywhere outside the <a> tag does nothing. This creates a misleading affordance. Either make the whole card a clickable link or remove the cursor: 'pointer'.

🤖 Prompt for AI Agents
In `@src/pages/projects.jsx` around lines 35 - 53, The card (MuiCard) sets cursor:
'pointer' but only the inner <a> is clickable, causing a misleading affordance;
either remove the cursor style from the MuiCard sx prop or make the entire card
clickable by moving the link/onclick to the MuiCard (e.g., wrap MuiCard with the
anchor/Link or add an onClick that navigates to the same href and set
role="link" and tabIndex for accessibility); update the MuiCard's sx to drop
cursor: 'pointer' if you choose not to make the whole card act as a link.


<CardContent sx={{ flexGrow: 1, textAlign: 'center' }}>
<Image
src={project.logo}
Expand Down Expand Up @@ -83,10 +90,16 @@ const Cards = () => {
</Typography>
</CardContent>
<CardActions sx={{ justifyContent: 'center' }}>
<p className="relative z-10 mt-6 flex text-md font-semibold font-mono text-zinc-600 transition group-hover:text-[#00843D] dark:group-hover:text-yellow-400 dark:text-zinc-200">
<LinkIcon className="h-6 w-6 flex-none scale-110" />
<span className="ml-2">{project.link.label}</span>
</p>
<a
href={project.link.href}
target="_blank"
rel="noopener noreferrer"
className="relative z-10 mt-6 flex text-md font-semibold font-mono text-zinc-600 dark:text-zinc-200 hover:text-[#00843D] dark:hover:text-yellow-400"
>
<LinkIcon className="h-6 w-6 flex-none scale-110" />
<span className="ml-2">{project.link.label}</span>
</a>

</CardActions>
</MuiCard>
</Grid>
Expand Down
13 changes: 13 additions & 0 deletions src/style/tailwind.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
.skeleton-card {
height: 180px;
width: 100%;
background: #2a2a2a;
border-radius: 8px;
animation: pulse 1.5s infinite ease-in-out;
}

@keyframes pulse {
0% { opacity: 0.6; }
50% { opacity: 1; }
100% { opacity: 0.6; }
}