Modern Vue.js portfolio built around a high-end editorial design system ("The Digital Archivist" — see designs/DESIGN.md), deployed to Google Cloud Run. You are currently viewing this project.
Cloud Specialist @ PointStar | Google Cloud Professional Certified
Full-Stack Developer building scalable web applications since 2021. Currently serving as a Cloud Specialist at PointStar, delivering Google Cloud Platform solutions across SEA.
I specialize in full-stack web development, AI/ML integration, and REST API development. My technical expertise spans modern JavaScript frameworks, backend development, and cloud-native architecture.
Key Achievements:
- Google Cloud Professional Developer Certified
- Technical Expert Badge - Build with Vertex AI
- Delivered enterprise solutions across cross-functional teams
- Editorial Design System: Warm, archival aesthetic with serif/sans typography pairing — rules documented in
designs/DESIGN.md - Custom Cursor: Dot-and-ring cursor in the site palette (mouse devices only, respects reduced-motion)
- Scroll-Triggered Animations: Sections animate in via a shared
useScrollTriggercomposable - Section-based Data Architecture: No static text in templates — all content organized in data files
- Web3Forms Integration: Functional contact form with real-time submission
- Development Tools: ESLint, Prettier, and Vitest configured for code quality
| Category | Technology |
|---|---|
| Framework | Vue.js 3 with Composition API |
| Styling | Tailwind CSS for responsive design |
| Animations | CSS3 transitions and keyframes for smooth animations |
| Icons | Lucide Vue for modern iconography |
| Build Tool | Vite for fast development and optimized builds |
| Testing | Vitest + Vue Test Utils |
| Dev Tools | ESLint + Prettier for code quality |
portfolio-website/
├── src/
│ ├── components/ # Reusable Vue.js components
│ │ ├── ui/ # Base UI components (BackToTop, ProjectCard, CustomCursor)
│ │ ├── sections/ # Page sections (Hero, About, Skills, Projects, Contact)
│ │ └── layout/ # Layout components (AppHeader, AppFooter)
│ ├── composables/ # Vue 3 composables (useScrollTrigger)
│ ├── data/ # Static data configuration organized by section
│ │ ├── hero.js # Hero section data (name, title, subtitle, buttons)
│ │ ├── contact.js # Contact section data + shared contact info
│ │ ├── footer.js # Footer data (imports from personal.js / contact.js)
│ │ ├── personal.js # About section data (bio, achievements, skills)
│ │ ├── skills.js # Technical skills grouped by category
│ │ └── projects.js # Project portfolio data
│ └── utils/ # Shared helpers (scroll.js)
├── designs/ # Design system documentation + mockups
├── public/ # Static public assets (images, favicon)
└── tests/ # Vitest test suite
To get a local copy up and running, follow these simple steps.
- Clone the repo
git clone https://github.com/KennethChua1998/Portfolio-Website.git
- Navigate to the project directory
cd Portfolio-Website - Install NPM packages
npm install
- Run the development server
npm run dev
The application will be available at http://localhost:8080.
# Development server with hot reload
npm run dev
# Build for production
npm run build
# Preview production build locally
npm run preview
# Serve production build
npm run serve
# Lint and fix code
npm run lint
# Run tests (watch mode; use `npx vitest run` for a single pass)
npm run test
# Run tests with UI
npm run test:ui
# Format code
npm run formatThis project is a single-page Vue 3 app — App.vue composes the section components directly (no router, no global store). Content lives in a modular data layer:
- Single File Components with
<script setup>syntax - Composition API with shared composables (
useScrollTrigger) and utils (utils/scroll.js) - Component-based architecture with clear separation of concerns
- Responsive design with Tailwind CSS tokens implementing the editorial design system
All data files are centralized in src/data/ with no static text in templates. personal.js is the source of truth for identity and the dynamically computed years of experience; hero.js and footer.js import from it, and footer.js shares contact links with contact.js.
- HeroSection.vue: Uses
heroDatafor headline, subtitle, and button text - AboutSection.vue: Uses
personalInfofor bio, achievements, and skills with scroll animations - SkillsSection.vue: Uses skill category exports with progress indicators
- ProjectsSection.vue: Uses
projectsarray for project cards with technology badges - ContactSection.vue: Uses
contactDatafor titles, contact info, and form labels - AppFooter.vue: Uses
footerDatafor footer content and social links
CRITICAL RULE: NO static text should be hardcoded in HTML/Vue templates!
All text content must be stored in the corresponding data files:
<!-- ✅ GOOD: Text from data file -->
<h1>{{ heroData.headline }}</h1>
<p>{{ heroData.subtitle }}</p>
<button>{{ heroData.buttons.contact }}</button><!-- ❌ BAD: Hardcoded static text -->
<h1>Kenneth Chua</h1>
<p>Cloud Specialist & Full-Stack Developer</p>
<button>Get In Touch</button>- Content Management: All content changes happen in one place (data files)
- Consistency: Prevents text duplication and inconsistencies
- Internationalization: Makes future i18n implementation easy
- Maintenance: Developers don't need to hunt through templates for text changes
Test suite using Vitest and Vue Test Utils.
tests/
├── components/ # Vue component tests (ProjectCard, BackToTop, AppFooter)
├── composables/ # Composable tests (useScrollTrigger)
├── data/ # Data validation tests (projects, skills, personal)
└── utils/ # Helper tests (scroll)
# Run all tests once
npx vitest run
# Watch mode
npm run test
# Run tests with UI
npm run test:ui
# Run specific test file
npx vitest tests/components/ProjectCard.test.jsThis project deploys automatically to Google Cloud Run (asia-southeast1) via GitHub Actions: tests + lint + build run on every push, and pushes to master build the Docker image and deploy it. The production bundle is built inside the Docker image — dist/ is not committed.
roles/run.developer- Deploy and manage Cloud Run services (not admin)roles/artifactregistry.writer- Push container imagesroles/iam.serviceAccountUser- Act as Cloud Run service account
GCP_PROJECT_ID- Your Google Cloud project IDGCP_SA_KEY- Service account key JSON file content
gcloud run deploy portfolio \
--source . \
--platform managed \
--region asia-southeast1 \
--allow-unauthenticated \
--port 8080Kenneth Chua - Cloud Specialist & Full-Stack Software Engineer
- 📧 Email: me@kennethchua.com
- 💻 GitHub
- 🌍 Location: Penang, Malaysia
- 🔗 Portfolio: Live Demo