Personal site for Daniel Fang — works, lab experiments, investments, and writing.
Live at danielfang.me.
| Layer | Technology |
|---|---|
| Framework | Astro 5 — static by default, React islands where needed |
| UI | React 18 for interactive components |
| Styling | Tailwind CSS + CSS custom properties for design tokens |
| Animation | GSAP (ScrollTrigger) and Lenis for smooth scroll |
| Content | Astro Content Collections — Markdown for works, MDX for blog, JSON for investments |
| Deployment | Netlify |
src/
├── pages/ # Routes: /, /works, /works/[slug], /lab, /blog, /blog/[slug],
│ # /investments, /contact, /404
├── layouts/ # BaseLayout (meta, nav, footer)
├── components/ # Nav, Footer, HeroSection, WorksCarousel, LabGrid,
│ ├── case-studies/ # interactive case studies for select works
│ ├── lab/ # shader / particle experiments
│ └── museum/ # museum-style media components
├── content/
│ ├── works/ # Project entries (Markdown + frontmatter)
│ ├── blog/ # Posts (MDX)
│ └── investments/ # Investment entries (JSON)
├── data/ # Static data (e.g. experiments index)
├── scripts/ # Client-side enhancements (magnetic CTA, etc.)
└── styles/ # global.css — tokens, base, utilities
public/ # Static assets — favicon, og image, images, videos, museum
| Command | Description |
|---|---|
npm run dev |
Start dev server at http://localhost:4321 |
npm run build |
Production build → dist/ |
npm run preview |
Preview the production build locally |
| Route | Description |
|---|---|
/ |
Landing — hero, works carousel, timeline |
/works |
Full works list |
/works/:slug |
Project detail with media gallery and optional interactive case study |
/lab |
Generative experiments (shaders, particles, vertex fields) |
/lab/:experiment |
Individual experiment page |
/blog |
Writing index |
/blog/:slug |
Post (MDX) |
/investments |
Investment portfolio |
/contact |
Contact + social links |
- Palette — Dark base (
#0A0A0A), surface shades, off-white text, single accent. - Typography — Display (Syne), body (Plus Jakarta Sans), mono (JetBrains Mono).
- Motion — Consistent easing (
cubic-bezier(0.16, 1, 0.3, 1)), durations 150–600ms; all animations respectprefers-reduced-motion.
Works, blog posts, and investments are defined as content collections in src/content/. See src/content/config.ts for the schema of each collection.
- Works — Add a
.mdfile undersrc/content/works/. Required frontmatter:title,description,role,year,stack,thumbnail. Setfeatured: trueto surface on the home carousel. - Blog — Add a
.mdxfile undersrc/content/blog/withtitle,date,excerpt, and optionaltags. - Investments — Add a
.jsonfile undersrc/content/investments/.
Privacy-first, cookieless analytics via Cloudflare Web Analytics — no cookies, no consent banner, no PII. The beacon only loads in production builds and only when PUBLIC_CF_BEACON_TOKEN is set.
To enable: copy .env.example to .env, then set PUBLIC_CF_BEACON_TOKEN from the Cloudflare dashboard (Analytics & Logs → Web Analytics → site → JS snippet → token value). In production, set the same variable in Netlify under Site settings → Environment variables. SPA navigations (Astro's ClientRouter) are tracked automatically.
Source code is available for reference. Content (writing, images, project descriptions) is © Daniel Fang. Please don't redeploy this site as your own.