A premium, animation-rich personal portfolio for Durga Prasad Yadav — AWS Accredited Instructor, AI/ML Trainer and Cloud Architect. Built as a fast, dependency-free static site (no build step, no npm install) so it can be edited and deployed directly from GitHub Pages.
Live structure: one flagship one-page experience (index.html) covering every section, plus four focused deep-dive pages (about.html, projects.html, blog.html, contact.html) that reuse the same design system and data.
The site pulls all personal content — name, bio, skills, projects, timeline, certifications, testimonials, blog posts, contact details — from two JSON files in data/. Nothing personal is hard-coded into the HTML, so the whole site can be updated by editing data, not markup.
Visual language: dark glassmorphism, gradient glows, floating blobs and a subtle animated 3D particle background, wrapped around Poppins / Space Grotesk / Inter typography.
- Hero with animated typing role rotator, glowing avatar ring and floating skill chips
- About with rich bio, highlight list and animated stat counters
- Skills with tabbed categories and animated circular progress rings
- Services, Projects (filterable grid), Timeline (Experience/Education tabs), Certifications
- Testimonials carousel (Swiper), YouTube panel, Blog grid, Contact form with client-side validation
- Custom animated cursor + mouse-follow glow (disabled automatically on touch devices)
- Three.js particle field + drifting wireframe mesh background
- Lenis smooth scrolling wired into GSAP's ticker (single driver — see Notes on Lenis below)
- GSAP + ScrollTrigger entrance animations, AOS scroll reveals, Vanilla-Tilt hover tilt on cards
- Dark/light theme toggle (persisted in
localStorage), sticky navbar with scroll-spy, mobile slide-in menu - Page loader, scroll-progress bar, back-to-top button
- Fully responsive (desktop / tablet / mobile), respects
prefers-reduced-motion
Plain HTML5, CSS3 and vanilla ES6 — no framework, no bundler. Third-party libraries are loaded via CDN <script>/<link> tags:
| Library | Purpose |
|---|---|
| GSAP + ScrollTrigger | Scroll-driven entrance animations |
| Three.js | Particle field / wireframe background |
| Lenis | Smooth scrolling |
| Typed.js | Hero role typing effect |
| AOS | Scroll reveal for section headers |
| Swiper | Testimonials carousel |
| Vanilla-Tilt.js | 3D hover tilt on project/YouTube cards |
| Font Awesome 6 | Icons |
The site fetches data/*.json at runtime, and browsers block fetch() against file:// URLs — so it must be served over HTTP, even locally.
# from the project root, pick any one:
npx serve . # Node
python -m http.server # Python 3
php -S localhost:8000 # PHPThen open the printed local URL (e.g. http://localhost:3000).
- Push this folder to a GitHub repository (e.g.
cloudurga/cloudurga). - Go to Settings → Pages.
- Under Build and deployment, set Source to
Deploy from a branch, branchmain, folder/ (root). - Save — GitHub will publish at
https://cloudurga.github.io/cloudurga/.
No build step is required; the repository is the deployed site.
Everything personal lives in two files — edit these, not the HTML:
data/profile.json— name, titles, bio, stats, skills, services, timeline, certifications, testimonials, YouTube info, blog posts, contact details, social links, resume/photo paths.data/projects.json— the project/program cards shown in the Projects grid, including category tags used by the filter buttons.
Other common tweaks:
- Resume PDF — replace
assets/resume/Durga_Prasad_Yadav_Resume.pdfand updatepersonal.resumeinprofile.jsonif you rename it. - Profile photo — replace
assets/images/avatar.svgwith a real photo (any format) and updatepersonal.photo. - Colors / fonts — all design tokens live in
assets/css/variables.css. - Contact form backend — GitHub Pages can't run server code. By default the form falls back to opening the visitor's email client with a pre-filled message. To receive submissions directly, create a free endpoint at Formspree (or Getform/Basin) and set it on the form element:
<form id="contact-form" data-endpoint="https://formspree.io/f/xxxxxxx" ...>inindex.htmlandcontact.html. - YouTube videos — add
{ "id": "VIDEO_ID", "title": "..." }entries toyoutube.videosinprofile.jsonto embed them; leave the array empty to show the "coming soon" placeholder.
portfolio/
├── index.html # Flagship one-page site (all sections)
├── about.html # About + Skills + Journey + Certifications deep-dive
├── projects.html # Full filterable project grid + services
├── blog.html # Blog grid + YouTube panel
├── contact.html # Contact form + info + map
├── assets/
│ ├── css/ # variables, base, layout, components, animations, responsive → main.css
│ ├── js/ # data-loader, loader, nav, cursor, particles-bg, animations, carousel, contact-form, main
│ ├── images/ # avatar.svg and other imagery
│ ├── icons/ # favicon.svg
│ ├── resume/ # downloadable resume PDF
│ ├── certificates/ # (optional) certificate images if you want photo-based cards
│ └── videos/ # (optional) local video assets
├── data/
│ ├── profile.json # all personal content
│ └── projects.json # project/program cards
├── .claude/launch.json # local dev-server config (used by the Claude Code preview tool)
├── README.md
└── LICENSE
- Semantic HTML5 landmarks (
header,main,section,footer) throughout. - CSS split by concern (tokens / reset / layout / components / motion / responsive) and imported once via
main.css. - JS split into single-purpose modules; nothing runs until
DOMContentLoaded, and rendering the JSON data fires adata:readyevent that every animation module listens for before touching the DOM. - No inline styles/scripts beyond the JSON-LD structured-data block and one small CSS override on a page CTA.
prefers-reduced-motiondisables the particle background, GSAP/AOS entrances and reveal transitions.
Lenis needs exactly one driver calling lenis.raf() per animation frame. This project drives it from gsap.ticker (the pattern GSAP's own docs recommend) — do not also add a separate requestAnimationFrame loop calling lenis.raf(), as running both simultaneously feeds Lenis two conflicting time bases per frame and makes scrollTo() overshoot badly. main.js also calls lenis.resize() once the JSON data has finished rendering, since Lenis measures the page's scrollable height at construction time — before the async content injection has happened — and won't know about the new height otherwise.
MIT — see LICENSE.