-
-
Notifications
You must be signed in to change notification settings - Fork 353
UI skeleton 569 #571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
UI skeleton 569 #571
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| 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" /> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Artificial 800ms delay when data is already synchronously available. The 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 |
||
|
|
||
| useEffect(() => { | ||
| setRandomProjects(projects.sort(() => 0.5 - Math.random()).slice(0, 3)) | ||
|
|
@@ -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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Invalid
🐛 Fix- boxShadow: '1px 2px 1px 1px rgba(255,255,0,255)',
+ boxShadow: '1px 2px 1px 1px rgba(255, 255, 0, 1)',📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| }} | ||||||||||||||||||
| > | ||||||||||||||||||
|
Comment on lines
+35
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The pointer cursor appears over the entire card surface, yet clicking anywhere outside the 🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| <CardContent sx={{ flexGrow: 1, textAlign: 'center' }}> | ||||||||||||||||||
| <Image | ||||||||||||||||||
| src={project.logo} | ||||||||||||||||||
|
|
@@ -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> | ||||||||||||||||||
|
|
||||||||||||||||||
| 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; } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 inindex.jsx, making it redundant here on each individual card.♻️ Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents