Imported from snow-viktor/personal-website (
AGENTS.md). Install upstream withnpx skills add snow-viktor/personal-website. Copyright stays with the author.
personal-website
Astro 7.x static site, deployed to Cloudflare Pages (snow-viktor.pages.dev). Scripts are only dev / build / preview / astro — no lint, typecheck, or tests. No CI workflows, no opencode.json.
Skills (load before coding, most specific first)
Skills live in .agents/skills/ (gitignored; restore with npx skills experimental_install from skills-lock.json).
| Task | Skill |
|---|---|
| New page/section, landing, portfolio look, redesign | design-taste-frontend (primary), frontend-design for visual direction |
| Polish, animation, micro-interactions, "feel" | emil-design-eng |
| Critique / audit / improve existing UI | impeccable |
| Accessibility / UX compliance review | web-design-guidelines |
| SEO, ranking, meta tags, Core Web Vitals | seo-audit |
Context files (read before UI work)
PRODUCT.md— product truth (audience, archive-first principles). Don't invent bio/credentials/testimonials. Note: itsdraftand filename claims are stale (see below).DESIGN.md— normative tokens + named rules (Solid Text, Atmosphere, Marker, One Accent, Serif Voice, Flat-By-Default). Update it when adding tokens/sizes.
Commands
| Command | Action |
|---|---|
npm run dev |
dev server with HMR |
npm run build |
static build to dist/ |
npm run preview |
preview build locally (run build first) |
Verify UI edits with the detector (must return []):
& ".agents\skills\impeccable\scripts\impeccable.cmd" detect --json src public
Structure
src/config.ts— single source of truth forSITE,NAV_LINKS,SOCIAL_LINKS,QUOTES,FONTS,MATH. Never hardcode these elsewhere;astro.config.mjsholds Astro-only settings (site, plugins, integrations).src/content.config.ts— Zod schemas: articles (title, date, tags,collection?, draft), projects (+type,links[]— nocollection).src/content/articles/— date-only filenames (YYYY-MM-DD.md, title from frontmatter);src/content/projects/— slug filenames (e.g.wall-e.md).- Articles index groups by
collectionwith a sticky category nav (scroll-spy via IntersectionObserver, text links never pills, ungrouped first, nav order mirrors section order). Projects index is a flat ungrouped grid. - Detail prev/next: projects pass
allProjects(date-desc) throughgetStaticPathsprops, mirroring articles. src/lib/content.tsis the only content entrypoint (getPublishedArticles(),getPublishedProjects(),groupByCollection()). Exception:src/pages/api/search-index.json.tscallsgetCollectiondirectly; draft filtering lives insidebuildSearchIndex.src/lib/utils.ts+cjk.ts—isCjkChar()is the only CJK detector;computeReadingTime(),truncate(),stripMarkdown()/stripForReading(),formatDate()(Intl.DateTimeFormat('zh-TW'), ISO indatetime). Never strip inline with ad-hoc regexes.- Alerts (
src/lib/alerts.ts), math (src/lib/mathml.ts) are build-time mdast plugins (wired inastro.config.mjs), zero client JS. Alert styles (.markdown-alert*) and search-result styles (.search-result*, injected viainnerHTML) must live insrc/styles/global.css(global, unscoped). - Math styles vendored in
src/styles/temml-latin-modern.css(@imported byglobal.css); fonts self-hosted inpublic/fonts/. To update Temml, re-copynode_modules/temml/dist/Temml-Latin-Modern.cssand re-apply the two/fonts/URL rewrites. - Diagrams are hand-authored
.svginsrc/assets/, imported directly in MDX inside<figure class="diagram">(role="img"+<title>, vertical flow only, dependency restated as prose below). No wrapper component, nomdx.tsregistration. Photos go in the same folder but render via<Image />fromastro:assets. src/components/mdx.tsdefault-exports{ BookInfo }for MDX;src/layouts/BaseLayout.astroowns lang (zh-Hant-TW), dark-only chrome, and all metadata fromSITE.
Conventions
- Path alias
@/*→src/*. Dark theme only via CSS vars inglobal.css. draft: trueexcludes everywhere — listings, detailgetStaticPaths(no draft routes built), search index.- Design system mirrors
DESIGN.md(enforced bydetect): no gradient text; no one-sided accent bars (full 1px tinted border); orange = actions/links, gold = metadata, green = collection, blue = book-note context; gradients in ambient surfaces only. - Anchored groups need
scroll-margin-topclearing both sticky bars (header + collection nav). src/lib/search-client.tsruns in the browser (may importcjk.tsonly). Never import server-only modules from client<script>blocks.
Gotchas
trailingSlash: 'always'— all internal links end with/(/articles/slug/).- MDX detail pages must render
<Content components={components} />— without the map, custom tags (BookInfo) throw at render time. .impeccable/config.jsonholds shared detector ignores (e.g. file-scopeddesign-system-fontwaiver for vendored Temml CSS) — commit it. Per-developer overrides live in.impeccable/config.local.json(covered by*.localin.gitignore).