Personal portfolio website for Luu Binh An, Senior Frontend Developer & Educator. Built with Gatsby 5 and React 18.
- Framework: Gatsby 5.13 (React 18.2)
- Styling: SCSS + CSS Modules + Bootstrap 4.6
- Animation: framer-motion 11.x
- Navigation: react-scroll for smooth section scrolling
- Node Version: 22.14.0
- Node.js 22.14.0 or higher
- npm or yarn
npm installnpm run dev
# or
npm run developRuns the development server at http://localhost:8000
npm run buildCreates an optimized production build.
npm run deploy
# or
npm run build:ghBuilds with path prefix and deploys to GitHub Pages.
npm run cleanRemoves the public directory.
src/
βββ components/ # Reusable React components (CSS Modules)
β βββ *.js # Component files
β βββ *.module.scss # CSS Module styles
βββ pages/ # Gatsby page components
β βββ index.js # Main landing page
β βββ 404.js # 404 error page
βββ sections/ # Page section components
βββ scss/ # Global SCSS styles
β βββ style.scss # Main stylesheet entry
β βββ bootstrap/ # Bootstrap customization
β βββ components/ # Component-specific styles
β βββ pages/ # Page-specific styles
βββ helpers/ # Utility functions
βββ images/ # Image assets
static/
βββ images/ # Static image assets (AVIF format)
The project uses CSS Modules as the primary styling approach:
// Import as default
import styles from './ComponentName.module.scss';
// Usage
<div className={styles.className}>- CSS Module files:
ComponentName.module.scssinsrc/components/ - Class names: camelCase (
.sectionWork, not.section-work) - Global classes: Use
:global(.className)for child elements receiving classes from props - Never use namespace imports:
import * as stylesβ
Always use the AnimatedDiv wrapper for framer-motion animations:
import AnimatedDiv from '../components/AnimatedDiv';
<AnimatedDiv
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
{/* content */}
</AnimatedDiv>Never import motion directly from framer-motion to avoid SSR issues.
- CSS Modules: Configured with
cssLoaderOptions.esModule: falseandnamedExport: false - ESLint: Disabled via webpack config override
- Sass warnings: Silenced for legacy deprecations
NETLIFY_GOOGLE_ANALYTICS_ID: Google Analytics tracking ID
- Single-page application with smooth scroll navigation
- Responsive design with Bootstrap grid system
- 16-column custom grid layout for portfolio items
- Animated section transitions with framer-motion
- Course showcase sections
- Blog integration
- Portfolio work gallery
The site is deployed to GitHub Pages. Use the following command to build and deploy:
npm run deployThis will:
- Build the site with path prefixes
- Deploy to the
gh-pagesbranch - Make it available at
https://luubinhan.github.io/
MIT License - Copyright (c) Luu Binh An
Luu Binh An
- Email: luubinhan1989@gmail.com
- Website: https://luubinhan.github.io/
- Blog: https://luubinhan.github.io/blog/