Personal portfolio site for Abdul Haseeb, a Senior Full-Stack Engineer. Built with Nuxt 4 (Vue 3, <script setup lang="ts">) and Tailwind CSS, and shipped as a statically generated site.
- Nuxt 4 (Vue 3, TypeScript) — statically generated (SSG)
- Tailwind CSS (
@nuxtjs/tailwindcss) + global CSS variables inassets/css/main.css - Nuxt modules:
@nuxt/fonts,@nuxt/image,nuxt-aos(scroll animations) - Vitest + jsdom for unit tests
- pnpm as the package manager
Requires pnpm (see the packageManager field in package.json).
pnpm install # install dependencies
pnpm dev # start the dev server on http://localhost:3000Copy .env.example to .env and adjust as needed (see Configuration).
| Command | What it does |
|---|---|
pnpm dev |
Start the dev server on http://localhost:3000 |
pnpm generate |
Production build — static site in .output/public (used for deployment) |
pnpm build / pnpm preview |
SSR server build + local preview |
pnpm test |
Run the Vitest unit tests once |
pnpm test:watch |
Run the tests in watch mode |
The deployed artifact is produced by
pnpm generate, notpnpm build.
app.vue # root — all SEO/useHead metadata (OG, Twitter, JSON-LD)
layouts/default.vue # global nav + footer; hosts the project-image lightbox
pages/
index.vue # home, composed of Home/* section components
projects/
index.vue # project gallery
<slug>.vue # one case-study page per project
PATTERN.md # the shared structure every project page follows
components/ # auto-imported by path (e.g. Home/, Project/, Icons/)
composables/ # auto-imported (e.g. useProjectObserver — scroll-spy TOC)
data/reviews.ts # typed client testimonials
utils/lightbox.ts # lightbox trigger logic (unit-tested)
assets/css/main.css # :root design tokens (colors, fluid type)
tests/ # Vitest unit tests
- Auto-imports: components are referenced by path —
components/Home/UpworkReviews.vue→<HomeUpworkReviews/>; theLazy*prefix lazy-loads. Composables andutils/are auto-imported too. - Project pages: every page under
pages/projects/shares one structure. Readpages/projects/PATTERN.mdbefore adding or editing one. - Upwork gating:
config.public.upwork(envNUXT_PUBLIC_UPWORK) toggles between showing social links and Upwork review cards/CTAs. - Styling: use the CSS variables in
assets/css/main.css(e.g.var(--primary-color)) rather than hardcoding colors.
Testing is deliberately minimal — Vitest + jsdom cover only genuine logic (e.g. utils/lightbox.ts), not presentational markup or copy.
pnpm testRuntime config is environment-driven (nuxt.config.ts → runtimeConfig.public). See .env.example:
| Variable | Purpose | Default |
|---|---|---|
NUXT_PUBLIC_UPWORK |
Upwork feature flag (hide socials, show Upwork reviews) | false |
NUXT_PUBLIC_DOMAIN |
Canonical domain for SEO (canonical/OG URLs) | zrinasoft.com |
NUXT_BASE_URL |
App base URL | / |
UPWORK_PROFILE_LINK |
Upwork profile URL | — |
Two targets, each on its own branch:
- GitHub Pages (
.github/workflows/github-pages.yml) — on push tomaster, runspnpm generatewithNUXT_PUBLIC_UPWORK=trueandNUXT_PUBLIC_DOMAIN=code-creeper.github.io, then publishes.output/publicto thegh-pagesbranch. - EC2 (
.github/workflows/deploy-ec2.yml) — on push toec2, SSHes into the host and rebuilds inside theportfolio_node_containerDocker container (Node 22 alpine, port 3000).
See CHANGELOG.md for notable changes.