feat: homepage personal portfolio (Phase 4)
- data/profile.yaml schema: name, handle, tagline, avatar, bio, links - data/projects.yaml schema: featured projects with title, tagline, repo, demo, image, tags - _partials/home/hero.html: avatar + name + tagline + bio + social links - _partials/home/projects.html: data-driven project grid - _partials/home/recent-posts.html: configurable count, view-all link - home.html composes hero → projects → recent-posts - assets/icons: github, mail, rss (Lucide-style) - exampleSite fixtures: profile, 4 projects, 5 SVG placeholder images
No files matched your search
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.11.79-.25.79-.56v-2c-3.2.7-3.88-1.36-3.88-1.36-.52-1.32-1.27-1.67-1.27-1.67-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.02 1.76 2.69 1.25 3.34.95.1-.74.4-1.25.73-1.54-2.55-.29-5.24-1.28-5.24-5.7 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.46.11-3.05 0 0 .96-.31 3.15 1.18a10.94 10.94 0 0 1 5.74 0c2.19-1.49 3.15-1.18 3.15-1.18.62 1.59.23 2.76.11 3.05.74.81 1.18 1.84 1.18 3.1 0 4.43-2.69 5.4-5.26 5.69.41.36.78 1.06.78 2.14v3.18c0 .31.21.68.8.56C20.21 21.39 23.5 17.08 23.5 12 23.5 5.65 18.35.5 12 .5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 704 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="2" y="4" width="20" height="16" rx="2"/>
|
||||
<path d="m22 6-10 7L2 6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 289 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M4 11a9 9 0 0 1 9 9"/>
|
||||
<path d="M4 4a16 16 0 0 1 16 16"/>
|
||||
<circle cx="5" cy="19" r="1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 312 B |
@@ -0,0 +1,18 @@
|
||||
name: "Tien Nguyen"
|
||||
handle: "tiennm99"
|
||||
tagline: "Building tools, breaking things, writing it down."
|
||||
avatar: "img/avatar.svg"
|
||||
bio: |
|
||||
Software engineer based in Ho Chi Minh City. I build for the web,
|
||||
experiment with AI tooling, and write down what I learn.
|
||||
|
||||
links:
|
||||
- icon: github
|
||||
label: GitHub
|
||||
url: https://github.com/tiennm99
|
||||
- icon: mail
|
||||
label: Email
|
||||
url: mailto:hi@miti99.com
|
||||
- icon: rss
|
||||
label: RSS
|
||||
url: /index.xml
|
||||
@@ -0,0 +1,25 @@
|
||||
featured:
|
||||
- title: "bonsai"
|
||||
tagline: "Minimalist Hugo theme for link-in-bio. Inspired by Linktree and Japanese bonsai aesthetics."
|
||||
repo: https://github.com/tiennm99/bonsai
|
||||
demo: https://tiennm99.github.io/bonsai/
|
||||
image: "img/projects/bonsai.svg"
|
||||
tags: [hugo, theme, minimal]
|
||||
|
||||
- title: "miti99bot"
|
||||
tagline: "Plug-n-play Telegram bot framework on Cloudflare Workers with grammY."
|
||||
repo: https://github.com/tiennm99/miti99bot
|
||||
image: "img/projects/miti99bot.svg"
|
||||
tags: [telegram, cloudflare, framework]
|
||||
|
||||
- title: "rplace"
|
||||
tagline: "Collaborative pixel art canvas inspired by Reddit r/place. Real-time WebSocket sync."
|
||||
repo: https://github.com/tiennm99/rplace
|
||||
image: "img/projects/rplace.svg"
|
||||
tags: [svelte, hono, durable-objects]
|
||||
|
||||
- title: "vngeoguessr"
|
||||
tagline: "GeoGuessr clone for Vietnamese locations. Mapillary street view + Redis leaderboards."
|
||||
repo: https://github.com/tiennm99/vngeoguessr
|
||||
image: "img/projects/vngeoguessr.svg"
|
||||
tags: [geo, mapillary, redis]
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" width="120" height="120">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#1a1a2e"/>
|
||||
<stop offset="100%" stop-color="#4a4a7e"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx="60" cy="60" r="60" fill="url(#g)"/>
|
||||
<text x="60" y="74" text-anchor="middle" font-family="system-ui,sans-serif" font-size="48" fill="#f5f5f5" font-weight="300">月</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 475 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 200" width="320" height="200">
|
||||
<rect width="320" height="200" fill="#0d1f17"/>
|
||||
<text x="160" y="110" text-anchor="middle" font-family="serif" font-size="64" fill="#7fb8a4">盆栽</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 254 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 200" width="320" height="200">
|
||||
<rect width="320" height="200" fill="#1f2937"/>
|
||||
<text x="160" y="118" text-anchor="middle" font-family="ui-monospace,monospace" font-size="40" fill="#60a5fa">miti99bot</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 274 B |
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 200" width="320" height="200">
|
||||
<rect width="320" height="200" fill="#fef3c7"/>
|
||||
<g>
|
||||
<rect x="40" y="40" width="40" height="40" fill="#ef4444"/>
|
||||
<rect x="80" y="40" width="40" height="40" fill="#22c55e"/>
|
||||
<rect x="120" y="40" width="40" height="40" fill="#3b82f6"/>
|
||||
<rect x="160" y="40" width="40" height="40" fill="#f59e0b"/>
|
||||
<rect x="200" y="40" width="40" height="40" fill="#a855f7"/>
|
||||
<rect x="240" y="40" width="40" height="40" fill="#06b6d4"/>
|
||||
<rect x="40" y="80" width="40" height="40" fill="#22c55e"/>
|
||||
<rect x="80" y="80" width="40" height="40" fill="#3b82f6"/>
|
||||
<rect x="120" y="80" width="40" height="40" fill="#ef4444"/>
|
||||
<rect x="200" y="80" width="40" height="40" fill="#06b6d4"/>
|
||||
<rect x="240" y="80" width="40" height="40" fill="#f59e0b"/>
|
||||
</g>
|
||||
<text x="160" y="160" text-anchor="middle" font-family="ui-monospace,monospace" font-size="22" fill="#1f2937">r/place</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 996 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 200" width="320" height="200">
|
||||
<rect width="320" height="200" fill="#064e3b"/>
|
||||
<circle cx="160" cy="100" r="50" fill="none" stroke="#34d399" stroke-width="3"/>
|
||||
<line x1="110" y1="100" x2="210" y2="100" stroke="#34d399" stroke-width="2"/>
|
||||
<line x1="160" y1="50" x2="160" y2="150" stroke="#34d399" stroke-width="2"/>
|
||||
<text x="160" y="180" text-anchor="middle" font-family="ui-monospace,monospace" font-size="18" fill="#a7f3d0">VN GeoGuessr</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 519 B |
@@ -0,0 +1,31 @@
|
||||
{{- $profile := site.Data.profile -}}
|
||||
{{- with $profile -}}
|
||||
<section class="home-hero" aria-labelledby="home-hero-name">
|
||||
{{- with .avatar }}
|
||||
<img class="home-hero-avatar" src="{{ . | relURL }}" alt="{{ $profile.name | default site.Title }}" width="120" height="120" loading="eager" decoding="async">
|
||||
{{- end }}
|
||||
|
||||
<h1 id="home-hero-name" class="home-hero-name">{{ .name | default site.Title }}</h1>
|
||||
|
||||
{{- with .tagline }}
|
||||
<p class="home-hero-tagline">{{ . }}</p>
|
||||
{{- end }}
|
||||
|
||||
{{- with .bio }}
|
||||
<div class="home-hero-bio">{{ . | markdownify }}</div>
|
||||
{{- end }}
|
||||
|
||||
{{- with .links }}
|
||||
<ul class="home-hero-links">
|
||||
{{- range . }}
|
||||
<li>
|
||||
<a href="{{ .url }}" rel="me noopener" aria-label="{{ .label | default .icon }}">
|
||||
{{- with .icon }}{{ partial "icon.html" . }}{{ end }}
|
||||
<span>{{ .label | default .icon }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
{{- end }}
|
||||
</section>
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,40 @@
|
||||
{{- with site.Data.projects.featured -}}
|
||||
<section class="home-projects" aria-labelledby="home-projects-heading">
|
||||
<h2 id="home-projects-heading" class="home-projects-heading">
|
||||
{{ i18n "featuredProjects" | default "Featured Projects" }}
|
||||
</h2>
|
||||
|
||||
<ul class="project-grid">
|
||||
{{- range . }}
|
||||
{{- $project := . }}
|
||||
<li class="project-card">
|
||||
{{- with $project.image }}
|
||||
<div class="project-card-image">
|
||||
<img src="{{ . | relURL }}" alt="{{ $project.title }}" loading="lazy" decoding="async">
|
||||
</div>
|
||||
{{- end }}
|
||||
|
||||
<h3 class="project-card-title">{{ $project.title }}</h3>
|
||||
{{- with $project.tagline }}
|
||||
<p class="project-card-tagline">{{ . }}</p>
|
||||
{{- end }}
|
||||
|
||||
{{- with $project.tags }}
|
||||
<ul class="project-card-tags">
|
||||
{{- range . }}<li>{{ . }}</li>{{ end }}
|
||||
</ul>
|
||||
{{- end }}
|
||||
|
||||
<ul class="project-card-links">
|
||||
{{- with $project.repo }}
|
||||
<li><a href="{{ . }}" rel="noopener">repo →</a></li>
|
||||
{{- end }}
|
||||
{{- with $project.demo }}
|
||||
<li><a href="{{ . }}" rel="noopener">demo →</a></li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
</li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
</section>
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,21 @@
|
||||
{{- $count := site.Params.home.recentPostsCount | default 5 -}}
|
||||
{{- $posts := first $count (where (where site.RegularPages "Type" "post") "Draft" false) -}}
|
||||
{{- with $posts -}}
|
||||
<section class="home-recent-posts" aria-labelledby="home-recent-heading">
|
||||
<h2 id="home-recent-heading" class="home-recent-heading">
|
||||
{{ i18n "recentPosts" | default "Recent Posts" }}
|
||||
</h2>
|
||||
|
||||
<ul class="post-list">
|
||||
{{- range . }}
|
||||
<li>{{ partial "post-card.html" . }}</li>
|
||||
{{- end }}
|
||||
</ul>
|
||||
|
||||
{{- if gt (len (where (where site.RegularPages "Type" "post") "Draft" false)) $count }}
|
||||
<p class="home-recent-more">
|
||||
<a href="{{ "/post/" | relURL }}">{{ i18n "viewAll" | default "View all" }} →</a>
|
||||
</p>
|
||||
{{- end }}
|
||||
</section>
|
||||
{{- end -}}
|
||||
@@ -1,9 +1,7 @@
|
||||
{{ define "body_class" }}home{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
{{/* Phase 4 fills hero/projects/recent-posts here. Placeholder for now. */}}
|
||||
<section class="home-placeholder">
|
||||
<h1>{{ site.Title }}</h1>
|
||||
{{- with site.Params.description }}<p>{{ . }}</p>{{ end }}
|
||||
</section>
|
||||
{{ partial "home/hero.html" . }}
|
||||
{{ partial "home/projects.html" . }}
|
||||
{{ partial "home/recent-posts.html" . }}
|
||||
{{ end }}
|
||||