initial hugo site with terminal theme
This commit is contained in:
parent
2c46b2ad6d
commit
ddf013b6b3
64
.gitea/workflows/ci.yaml
Normal file
64
.gitea/workflows/ci.yaml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# Required secrets:
|
||||||
|
# REGISTRY_USER — Gitea container registry username
|
||||||
|
# REGISTRY_PASSWORD — Gitea container registry password
|
||||||
|
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-check:
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Verify Hugo build
|
||||||
|
uses: docker://hugomods/hugo:exts
|
||||||
|
with:
|
||||||
|
args: hugo --minify
|
||||||
|
|
||||||
|
build-and-push:
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
driver: docker-container
|
||||||
|
driver-opts: network=host
|
||||||
|
|
||||||
|
- name: Log in to Gitea registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: gitea.nik4nao.com
|
||||||
|
username: ${{ secrets.REGISTRY_USER }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Resolve short SHA
|
||||||
|
id: sha
|
||||||
|
run: echo "short=$(echo ${{ github.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Build and push multiarch image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
gitea.nik4nao.com/nik/portfolio:latest
|
||||||
|
gitea.nik4nao.com/nik/portfolio:${{ steps.sha.outputs.short }}
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -1 +1,4 @@
|
|||||||
.env
|
.env
|
||||||
|
.hugo_build.lock
|
||||||
|
public/
|
||||||
|
resources/_gen/
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "themes/terminal"]
|
||||||
|
path = themes/terminal
|
||||||
|
url = https://github.com/panr/hugo-theme-terminal.git
|
||||||
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
FROM hugomods/hugo:exts AS builder
|
||||||
|
|
||||||
|
WORKDIR /site
|
||||||
|
COPY . .
|
||||||
|
RUN hugo --minify
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
COPY --from=builder /site/public /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
149
content/cv.md
Normal file
149
content/cv.md
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
---
|
||||||
|
title: "CV"
|
||||||
|
type: "cv"
|
||||||
|
date: 2026-03-17
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# NIK AFIQ
|
||||||
|
|
||||||
|
Tokyo, Japan
|
||||||
|
nik@nik4nao.com | github.com/nikafiq | nik4nao.com
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROFESSIONAL SUMMARY
|
||||||
|
|
||||||
|
Backend engineer with 3 years of professional experience designing and
|
||||||
|
operating distributed, high-throughput systems on GCP and AWS. Core
|
||||||
|
expertise in Go and Python, with hands-on production experience in
|
||||||
|
event-driven microservices, Kafka-based pipelines, Kubernetes, and
|
||||||
|
cloud-native data infrastructure. Comfortable operating systems at
|
||||||
|
hundreds of TPS with reliability and zero-downtime migration
|
||||||
|
constraints. Trilingual (English, Japanese N1, Malay) — routinely
|
||||||
|
bridges Japanese and overseas engineering teams. Actively integrates
|
||||||
|
AI tooling (GitHub Copilot, Gemini, Claude) into daily coding,
|
||||||
|
review, and documentation workflows.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WORK EXPERIENCE
|
||||||
|
|
||||||
|
### 株式会社ホープス (Hopes Co., Ltd.) — Tokyo
|
||||||
|
**Backend Engineer** | Aug 2025 – Present
|
||||||
|
|
||||||
|
Designing and operating a distributed RCS consent management pipeline
|
||||||
|
(SO→FoRCE) on GCP/GKE connecting a high-traffic notice delivery
|
||||||
|
system to a downstream fulfillment API.
|
||||||
|
|
||||||
|
- Architected an event-driven pipeline using GKE + Managed Kafka
|
||||||
|
(8 partitions, keyed by account_id) + Cloud Spanner, handling a
|
||||||
|
global cap of 200 TPS with a 10-second downstream timeout budget
|
||||||
|
- Built the Go consumer service (so-notice-receiver) with
|
||||||
|
singleflight coalescing to prevent duplicate in-flight requests,
|
||||||
|
and circuit breaker logic to shed load under downstream failure
|
||||||
|
- Designed reliable offset commit ordering: offsets committed only
|
||||||
|
after durable Spanner write, ensuring at-least-once delivery with
|
||||||
|
no data loss on crash
|
||||||
|
- Implemented a retry cronjob requeuing up to 5 failed Spanner rows
|
||||||
|
back to Kafka every 5 minutes, with configurable backoff
|
||||||
|
- Designed a zero-downtime interleaved index migration on a Cloud
|
||||||
|
Spanner accounts table under 400 TPS sustained read traffic
|
||||||
|
- Right-sized GKE resource configs (CPU/memory requests and limits)
|
||||||
|
from Locust load test data at 40 TPS steady / 120 TPS burst
|
||||||
|
- Propagated distributed traces across service boundaries for
|
||||||
|
end-to-end production observability
|
||||||
|
|
||||||
|
|
||||||
|
### 株式会社ニッポンダイナミックシステムズ — Tokyo
|
||||||
|
**Full Stack Engineer, IT Solutions — Pharma Market Team**
|
||||||
|
| Apr 2023 – Jul 2025
|
||||||
|
|
||||||
|
- Built a scalable analytical DWH on Amazon Aurora (RDS) for a
|
||||||
|
pharmaceutical client, integrating Salesforce and multiple
|
||||||
|
external data sources via daily/weekly ETL batch pipelines using
|
||||||
|
ECS/Fargate and Lambda; designed for HA with Multi-AZ failover
|
||||||
|
- Constructed a SaaS data lake using AWS CDK + Glue +
|
||||||
|
TypeScript/Python, fully automating ETL ingestion across
|
||||||
|
heterogeneous data sources
|
||||||
|
- Developed an internal AI application using AWS Bedrock (Claude
|
||||||
|
Sonnet) + React, implementing RAG-based document retrieval and
|
||||||
|
SES-based user matching in a small cross-functional team
|
||||||
|
- Built a license authentication service (Node.js + Docker + Azure
|
||||||
|
Web Apps + ADB2C), owning requirements definition, auth logic
|
||||||
|
design, and client-facing communication
|
||||||
|
- Designed and automated monthly maintenance operations: AMI image
|
||||||
|
updates, security patching, automated regression testing, and
|
||||||
|
blue/green deployments via AWS CodePipeline and Azure Pipelines
|
||||||
|
- Conducted Docker image vulnerability scanning as part of CI/CD
|
||||||
|
pipeline; managed VPC, WAF, and Security Group configurations
|
||||||
|
- Mentored junior engineers on cloud architecture patterns;
|
||||||
|
functioned as bilingual (EN/JA) liaison between domestic and
|
||||||
|
overseas engineering teams
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SKILLS
|
||||||
|
|
||||||
|
**Languages:** Go, Python, TypeScript/JavaScript
|
||||||
|
**Frameworks:** Gin, Flask, Next.js, Node.js
|
||||||
|
**Cloud — AWS:** ECS/Fargate, Lambda, Aurora/RDS, DynamoDB, Glue,
|
||||||
|
CDK, CodePipeline, Bedrock, Secrets Manager
|
||||||
|
**Cloud — GCP:** GKE, Cloud Spanner, Managed Kafka (Pub/Sub),
|
||||||
|
BigQuery, Cloud Trace
|
||||||
|
**Cloud — Azure:** Web Apps, ADB2C, Azure Pipelines
|
||||||
|
**Data:** MySQL, Aurora, PostgreSQL, DynamoDB, Cloud Spanner,
|
||||||
|
Kafka, Redis
|
||||||
|
**DevOps:** Docker, Kubernetes, ArgoCD, CI/CD, IaC (AWS CDK)
|
||||||
|
**Observability:** Distributed tracing, ELK stack, Kibana
|
||||||
|
**AI Tooling:** GitHub Copilot (daily coding + code review),
|
||||||
|
Gemini (documentation + research), Claude (architecture
|
||||||
|
reasoning + coding), AWS Bedrock RAG (production)
|
||||||
|
**Security:** VPC, WAF, Security Groups, Secrets Manager,
|
||||||
|
Docker vulnerability scanning
|
||||||
|
**Other:** Homelab (k3s, self-hosted services, Ansible/IaC),
|
||||||
|
personal dev blog at nik4nao.com
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CERTIFICATIONS
|
||||||
|
|
||||||
|
| Certification | Issued |
|
||||||
|
|---|---|
|
||||||
|
| AWS Certified Solutions Architect – Associate (SAA) | Oct 2024 |
|
||||||
|
| AWS Certified Developer – Associate (DVA) | Dec 2024 |
|
||||||
|
| AWS Certified Cloud Practitioner (CLF) | Apr 2024 |
|
||||||
|
| 基本情報技術者試験 (FE) — IPA Fundamental IT Engineer | Aug 2024 |
|
||||||
|
| JLPT N1 — Japanese Language Proficiency | Dec 2022 |
|
||||||
|
|
||||||
|
*In progress: AWS Solutions Architect – Professional (SAP),
|
||||||
|
Applied Information Technology Engineer (AP)*
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## EDUCATION
|
||||||
|
|
||||||
|
**Tokai University** — Bachelor of Engineering
|
||||||
|
Major: Electrical and Electronic Engineering
|
||||||
|
Minor: Information Technology
|
||||||
|
Graduated: March 2023
|
||||||
|
|
||||||
|
*During a COVID-related leave of absence (2020–2021), independently
|
||||||
|
studied programming and cloud architecture; resumed with an
|
||||||
|
added IT minor upon return.*
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ADDITIONAL
|
||||||
|
|
||||||
|
- **Languages:** English (business), Japanese (JLPT N1), Malay (native)
|
||||||
|
- **Homelab:** Self-hosted k3s cluster, Gitea, Jellyfin, Cloudflare
|
||||||
|
Tunnel, Ansible-based IaC on Minisforum UM790 Pro
|
||||||
|
- **Dev blog / personal site:** nik4nao.com
|
||||||
|
- **Self-hosted Git:** git.nik4nao.com (mirrored to github.com/nikafiq)
|
||||||
14
content/posts/hello-world.md
Normal file
14
content/posts/hello-world.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: "Hello World"
|
||||||
|
date: 2026-03-17
|
||||||
|
draft: false
|
||||||
|
tags: ["meta"]
|
||||||
|
---
|
||||||
|
|
||||||
|
Welcome to my corner of the internet.
|
||||||
|
|
||||||
|
This site is where I write about things I'm building, breaking, and learning — mostly homelabs,
|
||||||
|
Kubernetes, and the occasional software project. Expect posts about infrastructure, self-hosting,
|
||||||
|
and whatever rabbit hole I've fallen down most recently.
|
||||||
|
|
||||||
|
If you want to see what I'm currently working on, check out the [projects](/projects) page.
|
||||||
8
content/projects/_index.md
Normal file
8
content/projects/_index.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "Projects"
|
||||||
|
date: 2026-03-17
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
A collection of things I've built or am currently working on — mostly self-hosted infrastructure,
|
||||||
|
homelab experiments, and open-source tooling.
|
||||||
54
content/projects/homelab.md
Normal file
54
content/projects/homelab.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
title: "Homelab Kubernetes Cluster"
|
||||||
|
date: 2026-03-17
|
||||||
|
draft: false
|
||||||
|
description: "Self-hosted k3s cluster on bare-metal with Gitea CI/CD, multi-arch builds, Authentik SSO, and ~15 running workloads."
|
||||||
|
tags: ["kubernetes", "k3s", "homelab", "infrastructure", "traefik", "authentik"]
|
||||||
|
github: ""
|
||||||
|
url: ""
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
A self-hosted Kubernetes cluster running on bare-metal hardware at home. The cluster serves as a
|
||||||
|
platform for running personal services, experimenting with cloud-native tooling, and learning
|
||||||
|
operational patterns without a cloud bill.
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
|
||||||
|
| Host | Role | Specs |
|
||||||
|
|---|---|---|
|
||||||
|
| Minisforum UM780 XTX | K3s control-plane | AMD Ryzen 7 8745H |
|
||||||
|
| HP ProDesk (nik-debian) | K3s storage agent | NFS server, mergerfs media pool |
|
||||||
|
| Mac Mini M2 | Standalone Docker host | ARM, outside the cluster |
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- **Distribution:** k3s
|
||||||
|
- **Ingress:** Traefik v3
|
||||||
|
- **TLS:** cert-manager — Let's Encrypt (public) + internal CA (LAN)
|
||||||
|
- **Auth:** Authentik SSO — OIDC + forwardAuth proxy, TOTP MFA enforced
|
||||||
|
- **DNS:** Pihole (primary + secondary, externalIPs)
|
||||||
|
- **Storage:** NFS (Debian) + local-path dynamic provisioner
|
||||||
|
- **CI/CD:** Gitea Actions + act_runner, Docker buildx multiarch (amd64 + arm64)
|
||||||
|
- **Registry:** Gitea built-in container registry
|
||||||
|
- **Observability:** Prometheus + Grafana + Loki + Promtail
|
||||||
|
- **IaC:** Ansible (host-level), Helm + raw manifests (cluster-level), all tracked in Gitea
|
||||||
|
|
||||||
|
## Highlights
|
||||||
|
|
||||||
|
- All cluster state is managed as code in a Gitea monorepo — single-file manifests per service, organised by concern
|
||||||
|
- Authentik SSO protects all web-facing services via Traefik forwardAuth; OIDC integrated with Gitea and Grafana
|
||||||
|
- Multi-arch image builds (amd64 + arm64) via buildx on every push to `main`, pushed to the self-hosted registry
|
||||||
|
- Dual-cert TLS strategy: internal CA for `*.home.arpa` services, Let's Encrypt for `*.nik4nao.com` public services
|
||||||
|
- Pihole running as primary + secondary with externalIPs for LAN-wide DNS and ad-blocking
|
||||||
|
- DDNS CronJob keeps the public A record in sync via the Porkbun API
|
||||||
|
|
||||||
|
## Running Workloads
|
||||||
|
|
||||||
|
Traefik, cert-manager, Pihole, Authentik, Gitea, Prometheus, Grafana, Loki, Promtail,
|
||||||
|
Jellyfin, qBittorrent, JDownloader, Photoview, Dashy, Glances, DDNS CronJob, this portfolio site.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Active and in daily use.
|
||||||
54
hugo.toml
Normal file
54
hugo.toml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
baseURL = "https://nik4nao.com/"
|
||||||
|
languageCode = "en-us"
|
||||||
|
title = "nik4nao"
|
||||||
|
theme = "terminal"
|
||||||
|
|
||||||
|
[params]
|
||||||
|
# dir name of your main content (default is `content/posts`).
|
||||||
|
# the list of set content will show up on your index page (baseurl).
|
||||||
|
contentTypeName = "posts"
|
||||||
|
|
||||||
|
# ["orange", "blue", "red", "green", "pink"]
|
||||||
|
themeColor = "orange"
|
||||||
|
|
||||||
|
# if you set this to 0, only metadata of posts will appear
|
||||||
|
fullWidthTheme = false
|
||||||
|
|
||||||
|
# centered theme with any value (e.g. "true")
|
||||||
|
centerTheme = false
|
||||||
|
|
||||||
|
# if your resource directory contains an image called `cover.png`,
|
||||||
|
# it will be used as the cover of your blog
|
||||||
|
showCoverImage = true
|
||||||
|
|
||||||
|
# set terminal prompt
|
||||||
|
terminalPrompt = "nik@nik4nao ~ $"
|
||||||
|
|
||||||
|
# show/hide posts list on start page
|
||||||
|
showMenuItems = 3
|
||||||
|
|
||||||
|
# show a footer instead of the HUGO signature
|
||||||
|
# customFooter = ""
|
||||||
|
|
||||||
|
# set theme to full screen width
|
||||||
|
# fullWidthTheme = false
|
||||||
|
|
||||||
|
[[params.menu]]
|
||||||
|
identifier = "posts"
|
||||||
|
name = "posts"
|
||||||
|
url = "/posts"
|
||||||
|
|
||||||
|
[[params.menu]]
|
||||||
|
identifier = "projects"
|
||||||
|
name = "projects"
|
||||||
|
url = "/projects"
|
||||||
|
|
||||||
|
[[params.menu]]
|
||||||
|
identifier = "cv"
|
||||||
|
name = "cv"
|
||||||
|
url = "/cv"
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
tag = "tags"
|
||||||
|
|
||||||
|
paginate = 5
|
||||||
10
layouts/cv/single.html
Normal file
10
layouts/cv/single.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<article class="post">
|
||||||
|
<div class="cv-download">
|
||||||
|
<a href="/cv/nik-afiq-cv.pdf" download class="cv-download-btn">↓ Download PDF</a>
|
||||||
|
</div>
|
||||||
|
<div class="post-content">
|
||||||
|
{{ .Content }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
38
layouts/index.html
Normal file
38
layouts/index.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<div class="index-content">
|
||||||
|
|
||||||
|
{{/* ── Hero / intro ───────────────────────────────────────────────────── */}}
|
||||||
|
<div class="index-hero">
|
||||||
|
<p class="index-intro">
|
||||||
|
Backend engineer based in Tokyo. I build distributed systems,
|
||||||
|
operate Kubernetes infrastructure, and occasionally write about it.
|
||||||
|
</p>
|
||||||
|
<ul class="index-nav">
|
||||||
|
{{ range .Site.Params.menu }}
|
||||||
|
<li>
|
||||||
|
<span class="prompt">{{ $.Site.Params.terminalPrompt }}</span>
|
||||||
|
<a href="{{ .url }}">cd {{ .name }}</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{/* ── Recent posts ────────────────────────────────────────────────────── */}}
|
||||||
|
{{ $posts := where .Site.RegularPages "Type" .Site.Params.contentTypeName }}
|
||||||
|
{{ if $posts }}
|
||||||
|
<section class="index-posts">
|
||||||
|
<h2>Recent Posts</h2>
|
||||||
|
{{ range first 5 $posts }}
|
||||||
|
<article class="post-preview">
|
||||||
|
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "2006-01-02" }}</time>
|
||||||
|
<a href="{{ .Permalink }}">{{ .Title }}</a>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
{{ if gt (len $posts) 5 }}
|
||||||
|
<p class="index-all-posts"><a href="/posts">All posts →</a></p>
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
1
layouts/partials/extended_head.html
Normal file
1
layouts/partials/extended_head.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<link rel="stylesheet" href="/css/custom.css">
|
||||||
36
layouts/projects/list.html
Normal file
36
layouts/projects/list.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<div class="projects-page">
|
||||||
|
<header>
|
||||||
|
<h1 class="post-title">{{ .Title }}</h1>
|
||||||
|
{{ with .Content }}
|
||||||
|
<div class="page-intro">{{ . }}</div>
|
||||||
|
{{ end }}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="project-grid">
|
||||||
|
{{ range .Pages }}
|
||||||
|
<div class="project-card">
|
||||||
|
<h2 class="project-card__title">
|
||||||
|
<a href="{{ .Permalink }}">{{ .Title }}</a>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{{ with .Params.description }}
|
||||||
|
<p class="project-card__desc">{{ . }}</p>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Params.tags }}
|
||||||
|
<ul class="project-tags">
|
||||||
|
{{ range . }}<li>{{ . }}</li>{{ end }}
|
||||||
|
</ul>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<div class="project-card__links">
|
||||||
|
{{ with .Params.github }}<a href="{{ . }}" target="_blank" rel="noopener">GitHub →</a>{{ end }}
|
||||||
|
{{ with .Params.url }}<a href="{{ . }}" target="_blank" rel="noopener">Live →</a>{{ end }}
|
||||||
|
<a href="{{ .Permalink }}">Details →</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
32
layouts/projects/single.html
Normal file
32
layouts/projects/single.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
<article class="post">
|
||||||
|
<header class="project-header">
|
||||||
|
<h1 class="post-title">{{ .Title }}</h1>
|
||||||
|
|
||||||
|
{{ with .Params.description }}
|
||||||
|
<p class="project-description">{{ . }}</p>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Params.tags }}
|
||||||
|
<ul class="project-tags">
|
||||||
|
{{ range . }}<li>{{ . }}</li>{{ end }}
|
||||||
|
</ul>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if or .Params.github .Params.url }}
|
||||||
|
<div class="project-links">
|
||||||
|
{{ with .Params.github }}<a href="{{ . }}" target="_blank" rel="noopener">GitHub →</a>{{ end }}
|
||||||
|
{{ with .Params.url }}<a href="{{ . }}" target="_blank" rel="noopener">Live Site →</a>{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="post-content">
|
||||||
|
{{ .Content }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="post-footer">
|
||||||
|
<a href="/projects">← Back to Projects</a>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
216
static/css/custom.css
Normal file
216
static/css/custom.css
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
/* ─── CV page ─────────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.cv-download {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-download-btn {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.4rem 1rem;
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
transition: background 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cv-download-btn:hover {
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Projects list ────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.projects-page .page-intro {
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
color: var(--color);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card {
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
padding: 1.25rem 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card__title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card__title a {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card__title a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card__desc {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
opacity: 0.85;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Tags (shared by list and detail) ────────────────────────────────────── */
|
||||||
|
|
||||||
|
.project-tags {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-tags li {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 0.15rem 0.5rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Project links (shared) ───────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.project-card__links,
|
||||||
|
.project-links {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card__links a,
|
||||||
|
.project-links a {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-card__links a:hover,
|
||||||
|
.project-links a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Project detail page ──────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.project-header {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-description {
|
||||||
|
font-size: 1rem;
|
||||||
|
opacity: 0.85;
|
||||||
|
margin: 0.5rem 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-footer {
|
||||||
|
margin-top: 3rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-footer a {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-footer a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Homepage ─────────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.index-hero {
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-intro {
|
||||||
|
margin: 0 0 1.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-nav {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-nav li {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-nav .prompt {
|
||||||
|
opacity: 0.5;
|
||||||
|
margin-right: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-nav a {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-nav a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-posts h2 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-preview {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-preview time {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
opacity: 0.5;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-preview a {
|
||||||
|
color: var(--color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-preview a:hover {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-all-posts {
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-all-posts a {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index-all-posts a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
1
themes/terminal
Submodule
1
themes/terminal
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 5a2b4c0f1fdb9180d525930b2c8f68a90221d245
|
||||||
Loading…
x
Reference in New Issue
Block a user