master
Koko Blog
A fast, beautiful personal blog built with React, TypeScript, and a synthwave purple theme.
Load time target: < 100ms | Bundle target: < 50KB initial JS (gzipped) | Rendering approach: static-first + minimal runtime JS
Stack
- Framework: React 19 + TypeScript
- Build tool: Vite (fast dev server + optimized production bundles)
- Routing: React Router with route-level code splitting
- Content: Markdown posts validated by Zod at build time
- Syntax highlighting: Shiki at build time (no heavy client runtime highlighter)
- Image optimization:
node scripts/optimize-images.mjs(WebP + size variants via sharp) - Package manager: pnpm
For AI agents: This project is React-first. Use
.tsxcomponents and standard React patterns. Do not create non-React template formats. Keep dependencies lean and avoid heavy UI frameworks.
Quick Start
pnpm install
pnpm dev # dev server at http://localhost:5173
pnpm build # production build -> dist/
pnpm preview # preview production build
pnpm typecheck # TypeScript check (tsc --noEmit)
Project Structure
koko-blog/
├── public/
│ ├── images/
│ │ ├── posts/ # Original post images
│ │ └── previews/ # Optimized WebP variants
│ └── favicon.svg
├── src/
│ ├── app/
│ │ ├── App.tsx
│ │ └── routes.tsx # Route config + lazy loading
│ ├── components/
│ │ ├── layout/
│ │ │ ├── Layout.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ └── Footer.tsx
│ │ ├── Hero.tsx
│ │ ├── PostCard.tsx
│ │ └── PostGrid.tsx
│ ├── pages/
│ │ ├── HomePage.tsx
│ │ ├── PostPage.tsx
│ │ └── NotFoundPage.tsx
│ ├── content/
│ │ ├── config.ts # Zod schema for post frontmatter
│ │ └── blog/ # Markdown posts (.md files)
│ ├── styles/
│ │ ├── synthwave.css # Theme tokens + gradients
│ │ └── global.css # Reset + base styles
│ ├── lib/
│ │ ├── content.ts # Build-time content loading helpers
│ │ └── date.ts # Date formatting utilities
│ └── main.tsx
├── scripts/
│ └── optimize-images.mjs
├── vite.config.ts
├── tsconfig.json
├── README.md
└── TODO.md
Scripts
| Command | Description |
|---|---|
pnpm dev |
Start Vite dev server |
pnpm build |
Build production assets |
pnpm preview |
Preview production build |
pnpm typecheck |
Run TypeScript checks |
pnpm lint |
Lint codebase |
node scripts/optimize-images.mjs |
Convert and resize images before build |
Design System
Colors
| Token | Hex | Usage |
|---|---|---|
| Primary | #6a1b9a |
Buttons, accents, links |
| Primary Dark | #510ca9 |
Hover states |
| Secondary | #000080 |
Electronic sheen elements |
| Background | #000000 |
Page background |
| Paper | #121212 |
Card backgrounds |
| Text Primary | #ffffff |
Headings |
| Text Secondary | #eeeeee |
Body text |
| Text Muted | #888888 |
Metadata |
Typography
- Headlines: 2.5rem-4rem, weight 800
- Titles: 1.8rem, weight 700
- Body: 1rem, line-height 1.8
- Code: Monospace, dark theme blocks (Shiki)
Breakpoints
- Mobile: < 768px
- Tablet: 768px-1024px
- Desktop: > 1024px
Writing Content
Posts live in src/content/blog/ as .md files with YAML frontmatter:
---
title: "My Awesome Post"
slug: "my-awesome-post"
excerpt: "A brief description..."
publishedAt: "2026-02-28"
category: "Game Development"
tags: ["unity", "csharp"]
image: "/images/posts/hero.jpg"
draft: false
---
Your content here with **markdown** support.
TypeScript Path Aliases
import Layout from '@components/layout/Layout';
import '@styles/global.css';
Available aliases (defined in tsconfig.json):
@/*->./src/*@components/*->./src/components/*@pages/*->./src/pages/*@styles/*->./src/styles/*@lib/*->./src/lib/*
Performance Targets
| Metric | Target |
|---|---|
| Initial JS bundle | < 50KB (gzipped) |
| FCP | < 100ms (local baseline) |
| LCP | < 1.0s |
| CLS | < 0.1 |
| Lighthouse performance | > 90 |
Built with: React • TypeScript • Vite • pnpm
Description
Languages
TypeScript
42.9%
CSS
41.6%
JavaScript
13.2%
HTML
2.3%