The personal portfolio website of Ubaid Ahmad, a full-stack MERN developer and UI/UX designer. It is a single-page React site with ten sections, scroll-aware navigation, project and certificate carousels, a downloadable resume, and a contact form with two submit paths. All page content lives in plain data files, so updating the site never means editing components.
https://stackiid.github.io/portfolio/
- Features
- Page Sections
- Tech Stack
- Project Structure
- Prerequisites
- Getting Started
- Scripts
- Updating Content
- Architecture
- Design System
- Accessibility
- SEO
- Deployment
- Performance Considerations
- Known Limitations
- License
- Acknowledgements
- Ten-section single-page layout with smooth scrolling and a fixed navigation bar with a mobile menu
- Scroll-position dots on large screens that show the current section and jump to any section
- Active-section tracking built on
IntersectionObserver, with no scroll-percentage calculations - Scroll-reveal entrance animations powered by Anime.js
- Branded loading screen that stays for at least half a second
- Tabbed skills browser with arrow-key navigation
- Horizontal carousels for projects, certifications, collaborations, and testimonials, with previous and next arrows
- Auto-advance on the certifications and collaboration carousels on mobile only, paused while the user is interacting
- Project cards with a screenshot, description, technology tags, a live link, and a repository link
- Document viewer modal for certificates and the internship completion certificate
- Resume dialog that offers a download or an in-page view
- Testimonial cards with star ratings, and a hero rating badge whose average is computed from the testimonial data
- Contact form with an email path (posted to Formspree) and a WhatsApp path (opens a pre-filled
wa.melink), each with its own validation - Social links in the hero, the mobile menu, and the footer
- Respect for
prefers-reduced-motionin the stylesheet, the reveal animations, and the carousels
| Order | Section | Content |
|---|---|---|
| 1 | Hero | Greeting, role titles, tagline, social links, profile image, experience badge, and rating card |
| 2 | About | Short biography, three working principles, and a resume button |
| 3 | Skills | Five tabbed categories: Design, Frontend, Backend, Logic and CS, and Tools |
| 4 | Experience | Three entries covering an internship, freelance work, and university study |
| 5 | Education | Three entries from secondary school to university |
| 6 | Projects | Ten projects in a carousel |
| 7 | Certifications | Ten certificates, each openable in a viewer |
| 8 | Collaboration | Internship completion certificate with company logo and link |
| 9 | Testimonials | Four testimonials from colleagues and clients |
| 10 | Contact | Email and WhatsApp contact form |
| Category | Technology |
|---|---|
| UI library | React 19 (JavaScript with JSX) |
| Build tool | Vite 8 with @vitejs/plugin-react |
| Styling | Tailwind CSS 3 with PostCSS and Autoprefixer, using a custom theme |
| Animation | Anime.js 4 |
| Icons | Font Awesome 6.5.2, loaded from cdnjs with a Subresource Integrity hash |
| Fonts | Google Fonts: Space Grotesk (display), Plus Jakarta Sans (body), and Caveat (script accents) |
| Forms | Formspree (external endpoint) and a WhatsApp deep link |
| Linting | oxlint |
| Hosting | GitHub Pages through GitHub Actions |
| Backend | None |
portfolio/
|-- .github/workflows/
| `-- deploy.yml # Build and deploy to GitHub Pages
|-- public/
| |-- assets/
| | |-- images/ # Profile, avatars, project screenshots, credentials, logos
| | `-- resume/ # Resume PDF
| |-- favicon/ # SVG and PNG icons
| |-- robots.txt
| |-- sitemap.xml
| `-- site.webmanifest
|-- src/
| |-- components/
| | |-- cards/ # ProjectCard
| | |-- common/ # Loader, Footer, modals, section heading, carousel arrows, social links
| | |-- navigation/ # Navbar, MobileMenu, ScrollDots
| | `-- ui/ # Button
| |-- data/ # All site content (see Updating Content)
| |-- hooks/ # useActiveSection, useScrollReveal, useCarousel, useMediaQuery, useLockBodyScroll
| |-- sections/ # One folder per page section
| |-- utils/
| | `-- motion.js # Reduced-motion helper
| |-- App.jsx
| |-- index.css
| `-- main.jsx
|-- index.html
|-- postcss.config.js
|-- tailwind.config.js
|-- vite.config.js
|-- .oxlintrc.json
|-- package.json
|-- LICENSE
`-- README.md
- Node.js 20 or later (the GitHub Actions workflow uses Node 20)
- npm
- An internet connection for Google Fonts and Font Awesome
Clone the repository, install dependencies, and start the development server:
git clone https://github.com/stackiid/portfolio.git
cd portfolio
npm install
npm run devOpen the local URL that Vite prints. Because the build uses the base path /portfolio/, the site is served at http://localhost:5173/portfolio/.
| Command | What it does |
|---|---|
npm run dev |
Starts the Vite development server with hot reload |
npm run build |
Builds the production bundle to dist/ |
npm run preview |
Serves the production build locally |
npm run lint |
Runs oxlint |
Each section reads from a file in src/data/, so content changes do not touch component code.
| File | Controls |
|---|---|
profile.js |
Name, role titles, tagline, biography, working principles, profile image, and resume path |
skills.js |
Skill categories and the skills inside each |
experience.js |
Experience entries, including which one is marked current |
education.js |
Education entries |
projects.js |
Project cards: title, description, screenshot, live link, repository link, and technology tags |
certifications.js |
Certificate names, issuers, dates, and images |
collaborations.js |
Collaboration entries with logo, document image, and link |
testimonials.js |
Testimonials, avatars, and star ratings |
counters.js |
Numbers shown in the hero badge |
contact.js |
Formspree endpoint, WhatsApp number, and location |
socialLinks.js |
Social profile links |
Images go in public/assets/images/, and the resume PDF goes in public/assets/resume/.
- Single page:
App.jsxstacks the section components inside a<main>element, with the loader, navigation, scroll dots, footer, and resume dialog around it. There is no router; navigation uses in-page anchors such as#aboutand#projects - Data-driven sections: each section imports its data file and renders cards or lists from it
- Shared carousel logic:
useCarouselhandles scrolling by one item, previous and next availability, and optional auto-advance, and is reused by projects, certifications, collaborations, and testimonials - Reveal and tracking hooks:
useScrollRevealplays an Anime.js entrance animation once an element enters the viewport, anduseActiveSectionreports which section is in view - Reduced motion:
prefersReducedMotion()inutils/motion.jsis checked by the reveal and carousel code, and the stylesheet has a matching media query - Scroll offset: a global
scroll-margin-topkeeps section headings clear of the fixed navigation bar after an anchor jump
Design tokens are defined in tailwind.config.js.
| Group | Details |
|---|---|
| Colors | cream (warm background), teal (developer and trust), mango (designer and energy), and ink (warm near-black for text) |
| Typography | font-display (Space Grotesk), font-body (Plus Jakarta Sans), font-script (Caveat) |
| Shape and depth | A custom xl2 radius and two shadows, card and soft |
| Animation | A bounce-dot keyframe used by the loader |
Implemented practices visible in the code:
lang="en"on the root elementaria-labelon icon-only controls, andaria-hiddenon decorative iconsrole="dialog"andaria-modalon the modals, with Escape to close, scroll lock while open, and focus returned to the previously focused element- A real tab pattern in the skills browser (
role="tablist",role="tab",role="tabpanel") with arrow-key support aria-invalidandaria-describedbylinking contact form fields to their error messagesrole="status"on the loader and form feedback- Labeled regions for the carousels, and keyboard-reachable carousel arrows
- Visible
:focus-visiblestyles - A
prefers-reduced-motionmedia query that shortens animations and disables smooth scrolling
No accessibility audit or WCAG conformance level is claimed.
index.html includes a title, meta description, author, robots tag, canonical URL, Open Graph and Twitter Card tags (using the profile photo as the image), a schema.org Person JSON-LD block, favicon and touch icons, and a web app manifest. The public folder also contains robots.txt and sitemap.xml.
.github/workflows/deploy.yml builds the site and deploys it to GitHub Pages on every push to main. It installs with npm ci, runs npm run build, and publishes dist/ with the official Pages actions. The workflow does not run the linter.
One-time setup: in the repository, open Settings, then Pages, and set the source to GitHub Actions.
vite.config.js sets base: "/portfolio/", which matches serving the site from the portfolio repository path. If the repository is renamed, update this value.
- Project screenshots are WebP files
- Fonts use
preconnecthints anddisplay=swap, and the Font Awesome stylesheet carries an integrity hash - The reveal and active-section code uses
IntersectionObserverinstead of scroll listeners - Carousel auto-advance is limited to mobile and is skipped when reduced motion is requested
- The whole site ships as one JavaScript bundle, with no code splitting
- Certificate images are loaded at full size
- The SEO files assume the site lives at
https://stackiid.github.io/(canonical URL, sitemap,robots.txt, structured data, and manifeststart_url), while the build base path is/portfolio/. The sitemap androbots.txtare published under/portfolio/, but crawlers look forrobots.txtat the domain root - Image paths are joined inconsistently: the project screenshots and resume use
BASE_URLwithout an extra slash, while other images add one, which produces a double slash in the URL src/data/counters.jsdefinesprojectsShippedandhappyClients, but onlyyearsExperienceis shown on the page- Project descriptions are written by hand and can drift from the projects themselves; for example, the Study Station card lists 13 courses and 5 video channels, while that project's data files hold 10 courses and 82 channels
- Form submissions depend on the external Formspree endpoint, and the WhatsApp path only opens a link
- The site needs an internet connection to load fonts and icons
- There is no dark mode, no per-section page titles, and no automated tests
- The Open Graph image is the profile photo rather than a dedicated social card
This repository is not open source. The LICENSE file is an all-rights-reserved license: the code may be viewed and studied for personal learning and reference, but copying, modifying, redistributing, using it in commercial or client-facing products, or republishing it under another name requires prior written permission from the author. Contact details for permission requests are in the license file.
Copyright 2026 Ubaid Ahmad
- Animation library: Anime.js
- Styling utilities from Tailwind CSS
- Typefaces from Google Fonts: Space Grotesk, Plus Jakarta Sans, and Caveat
- Icons from Font Awesome
- Contact form handling by Formspree
- Everyone who contributed testimonials and collaborated on projects