feat: update portfolio with Simmonds Ltd design, light/dark theme, improved navigation
- Add Simmonds Ltd inspired design with grid patterns - Light/Dark theme toggle (default: light) - Center VNDK logo on main landing page - Move view mode toggle to Selected Works section - Highlight Download CV button with phosphor green - Remove duplicate view toggles from nav bar - Update Professional Journey with longer descriptions - Update README with new features
This commit is contained in:
parent
c586f24432
commit
bb9e8c9b81
5 changed files with 932 additions and 476 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
node_modules/
|
||||
dist/
|
||||
.DS_Store
|
||||
.env
|
||||
*.local
|
||||
45
README.md
45
README.md
|
|
@ -1,11 +1,33 @@
|
|||
# Portfolio
|
||||
# KHOA.VO Portfolio
|
||||
|
||||
My personal portfolio website built with React, Vite, and Tailwind CSS. Features dual-mode navigation - a creative editorial design view and a developer terminal interface, along with a specialized Print-Ready A4 CV crafted in a high-contrast editorial aesthetic.
|
||||
Personal portfolio website featuring dual personas (Creative & IT), inspired by Simmonds Ltd design aesthetics.
|
||||
|
||||
## Live Site
|
||||
|
||||
- **Main**: https://khoavo.myds.me
|
||||
- **Creative Works**: https://portfolio.khoavo.myds.me
|
||||
- **Creative Works**: https://portfolio.khoavo.myds.me (redirects to main)
|
||||
|
||||
## Features
|
||||
|
||||
### Creative Side
|
||||
- **Three viewing modes**: Grid, List, Minimal
|
||||
- **Image effects**: Grayscale + pixelated + blur → Full color on hover
|
||||
- **Enhanced project modal**: Keyboard navigation (ESC, Arrow keys)
|
||||
- **Professional Journey**: Longer, more detailed for HR/readability
|
||||
|
||||
### IT Side
|
||||
- **Retro desktop UI**: Draggable windows
|
||||
- **CRT screen effects**: Scanlines, vignette
|
||||
- **Idle screensaver**: 5s timeout with animated logo
|
||||
|
||||
### Design
|
||||
- **Simmonds Ltd inspired**: Dark/light theme, grid patterns, phosphor green accents
|
||||
- **Typography**: Syne (display) + IBM Plex Mono
|
||||
- **Default theme**: Light
|
||||
|
||||
### Print CV
|
||||
- Separate print-optimized A4 format
|
||||
- Concise professional journey
|
||||
|
||||
## Tech Stack
|
||||
|
||||
|
|
@ -28,22 +50,9 @@ npm run dev
|
|||
npm run build
|
||||
```
|
||||
|
||||
## Preview
|
||||
## Print CV
|
||||
|
||||
```bash
|
||||
npm run preview
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
Deployed on Synology NAS via Docker.
|
||||
|
||||
## AI Agent Optimization (AIO)
|
||||
|
||||
This portfolio is heavily optimized for Agentic SEO. It includes:
|
||||
- **JSON-LD Structured Data**: Explicitly defining `Person`, `jobTitle`, `knowsAbout`, and `seeks` (seeking job opportunity) metadata injected securely into `<head>`.
|
||||
- **AI-Friendly `robots.txt`**: Access explicitly granted to major LLM scrapers such as `GPTBot`, `PerplexityBot`, `ClaudeBot`, and `Google-Extended`.
|
||||
- **`llms.txt` Endpoint**: Available directly at `/llms.txt`, exposing an exact markdown-structured context endpoint purely for LLM ingestors to natively understand my professional profile.
|
||||
Press the "Download CV" button or use `window.print()` to generate the PDF CV.
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
|||
1135
src/App.jsx
1135
src/App.jsx
File diff suppressed because it is too large
Load diff
169
src/index.css
169
src/index.css
|
|
@ -1,24 +1,84 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Newsreader:ital,opsz,wght@0,6..72,200..800;1,6..72,200..800&family=JetBrains+Mono:wght@300;400;500;600&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--bg-primary: #F4F4F2;
|
||||
--bg-secondary: #EBEBE9;
|
||||
--text-primary: #1A1A1A;
|
||||
--text-secondary: #4A4A4A;
|
||||
--text-muted: #8E8E8E;
|
||||
--accent: #1A1A1A;
|
||||
--accent-subtle: rgba(26, 26, 26, 0.05);
|
||||
--border: rgba(0, 0, 0, 0.1);
|
||||
--grid-color: rgba(0, 0, 0, 0.03);
|
||||
--accent-it: #00FF94;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--bg-primary: #0D0D0D;
|
||||
--bg-secondary: #141414;
|
||||
--text-primary: #EAEAEA;
|
||||
--text-secondary: #A0A0A0;
|
||||
--text-muted: #606060;
|
||||
--accent: #EAEAEA;
|
||||
--accent-subtle: rgba(234, 234, 234, 0.05);
|
||||
--border: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #FAFAFA;
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
transition: background-color 0.5s cubic-bezier(0.4, 0, 0.2, 1), color 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.font-serif {
|
||||
font-family: 'Playfair Display', Georgia, serif;
|
||||
.noise-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
opacity: 0.06;
|
||||
z-index: 9999;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.font-mono {
|
||||
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
||||
.font-creative-sans {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.font-creative-serif {
|
||||
font-family: 'Newsreader', serif;
|
||||
}
|
||||
|
||||
.font-it-mono {
|
||||
font-family: 'JetBrains Mono', 'IBM Plex Mono', monospace;
|
||||
}
|
||||
|
||||
/* Legacy mappings to prevent breakage during transition */
|
||||
.font-display { @apply font-creative-sans; }
|
||||
.font-serif { @apply font-creative-serif; }
|
||||
.font-mono { @apply font-it-mono; }
|
||||
.font-body { @apply font-it-mono; }
|
||||
|
||||
/* Grid background pattern */
|
||||
.bg-grid-pattern {
|
||||
background-image: linear-gradient(rgba(0,0,0,0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0,0,0,0.03) 1px, transparent 1px);
|
||||
background-size: 60px 60px;
|
||||
}
|
||||
|
||||
.bg-dotted-pattern {
|
||||
background-image: radial-gradient(rgba(0,0,0,0.1) 1px, transparent 1px);
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
/* Screen: hide print-only version */
|
||||
|
|
@ -72,17 +132,14 @@ body {
|
|||
color: black !important;
|
||||
}
|
||||
|
||||
/* Hide the main interactive portfolio */
|
||||
.app-main {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide the preview overlay */
|
||||
.fixed.inset-0 {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Show only the print version */
|
||||
.print-only-version {
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
|
|
@ -126,7 +183,6 @@ body {
|
|||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
/* Cover Page */
|
||||
.pdf-content .cover-page {
|
||||
background: #111827 !important;
|
||||
display: flex !important;
|
||||
|
|
@ -185,7 +241,6 @@ body {
|
|||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
/* Sections */
|
||||
.pdf-content .section {
|
||||
margin-bottom: 7mm !important;
|
||||
}
|
||||
|
|
@ -201,7 +256,6 @@ body {
|
|||
letter-spacing: 0.05em !important;
|
||||
}
|
||||
|
||||
/* Summary */
|
||||
.pdf-content .summary-section {
|
||||
background: #f9fafb !important;
|
||||
padding: 5mm !important;
|
||||
|
|
@ -215,7 +269,6 @@ body {
|
|||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* Experience */
|
||||
.pdf-content .experience-list {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
|
|
@ -274,7 +327,6 @@ body {
|
|||
margin-bottom: 1.5mm !important;
|
||||
}
|
||||
|
||||
/* Skills */
|
||||
.pdf-content .skills-grid {
|
||||
display: grid !important;
|
||||
grid-template-columns: 1fr 1fr !important;
|
||||
|
|
@ -301,7 +353,6 @@ body {
|
|||
line-height: 1.5 !important;
|
||||
}
|
||||
|
||||
/* Portfolio */
|
||||
.pdf-content .portfolio-grid {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
|
|
@ -357,7 +408,6 @@ body {
|
|||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* Contact Page */
|
||||
.pdf-content .contact-page {
|
||||
background: #111827 !important;
|
||||
display: flex !important;
|
||||
|
|
@ -429,7 +479,7 @@ body {
|
|||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #0a0a0a;
|
||||
background: #0A0A0A;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
|
|
@ -440,3 +490,88 @@ body {
|
|||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #00D9FF;
|
||||
}
|
||||
|
||||
/* Image effects - Grayscale + pixelated to color */
|
||||
.grayscale-blur {
|
||||
filter: grayscale(80%) blur(1px);
|
||||
image-rendering: pixelated;
|
||||
transition: all 0.5s ease-out;
|
||||
}
|
||||
|
||||
.group:hover .grayscale-blur,
|
||||
.grayscale-blur:hover {
|
||||
filter: grayscale(0%) blur(0);
|
||||
image-rendering: auto;
|
||||
}
|
||||
|
||||
/* CRT Screen Effects */
|
||||
.crt-screen::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15),
|
||||
rgba(0, 0, 0, 0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.crt-screen::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: radial-gradient(ellipse at center, transparent 0%, rgba(0, 0, 0, 0.3) 100%);
|
||||
pointer-events: none;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
@keyframes crt-flicker {
|
||||
0%, 100% { opacity: 1; }
|
||||
92% { opacity: 1; }
|
||||
93% { opacity: 0.8; }
|
||||
94% { opacity: 1; }
|
||||
}
|
||||
|
||||
.crt-flicker {
|
||||
animation: crt-flicker 0.15s infinite;
|
||||
}
|
||||
|
||||
@keyframes crt-scanline {
|
||||
0% { transform: translateY(-100%); }
|
||||
100% { transform: translateY(100%); }
|
||||
}
|
||||
|
||||
.crt-scanline::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: rgba(0, 255, 148, 0.1);
|
||||
animation: crt-scanline 4s linear infinite;
|
||||
pointer-events: none;
|
||||
z-index: 1002;
|
||||
}
|
||||
|
||||
/* Screensaver animations */
|
||||
@keyframes blink {
|
||||
0%, 50% { opacity: 1; }
|
||||
51%, 100% { opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-5px); }
|
||||
}
|
||||
|
||||
.animate-blink {
|
||||
animation: blink 1s step-end infinite;
|
||||
}
|
||||
|
||||
.animate-float {
|
||||
animation: float 2s ease-in-out infinite;
|
||||
}
|
||||
|
|
@ -4,8 +4,52 @@ export default {
|
|||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {},
|
||||
extend: {
|
||||
colors: {
|
||||
bg: {
|
||||
primary: 'var(--bg-primary)',
|
||||
secondary: 'var(--bg-secondary)',
|
||||
},
|
||||
text: {
|
||||
primary: 'var(--text-primary)',
|
||||
secondary: 'var(--text-secondary)',
|
||||
muted: 'var(--text-muted)',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'var(--accent)',
|
||||
subtle: 'var(--accent-subtle)',
|
||||
it: '#00FF94',
|
||||
itSubtle: 'var(--accent-it-subtle)',
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
display: ['Syne', 'sans-serif'],
|
||||
body: ['IBM Plex Mono', 'monospace'],
|
||||
mono: ['JetBrains Mono', 'monospace'],
|
||||
serif: ['Playfair Display', 'Georgia', 'serif'],
|
||||
},
|
||||
typography: {
|
||||
display: {
|
||||
fontSize: ['clamp(3rem, 10vw, 10rem)', { lineHeight: '0.9', letterSpacing: '-0.02em', fontWeight: '800' }],
|
||||
},
|
||||
huge: {
|
||||
fontSize: ['clamp(2rem, 6vw, 6rem)', { lineHeight: '1', letterSpacing: '-0.02em', fontWeight: '700' }],
|
||||
},
|
||||
},
|
||||
backgroundImage: {
|
||||
'grid-pattern': 'linear-gradient(var(--grid-color) 1px, transparent 1px), linear-gradient(90deg, var(--grid-color) 1px, transparent 1px)',
|
||||
'dotted-pattern': 'radial-gradient(var(--border) 1px, transparent 1px)',
|
||||
},
|
||||
backgroundSize: {
|
||||
'grid': '60px 60px',
|
||||
'dotted': '20px 20px',
|
||||
},
|
||||
transitionDuration: {
|
||||
'400': '400ms',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue