6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
npm run dev— local dev server atlocalhost:4321npm run build— production build to./dist/npm run preview— preview production buildnpm run astro -- --help— Astro CLI (e.g.astro checkfor type-checking)
Node >= 22.12.0 required.
Path aliases
tsconfig.json defines a single ~/* → src/* alias. Prefer it over relative imports (~/components/Foo.astro instead of ../../components/Foo.astro) in .astro, .ts, .js, .mjs, and MDX files. Frontmatter heroImage paths and inline markdown  image refs stay relative — Astro's image resolver expects those relative to the content file.
Architecture
Astro 6 static site, based on the blog starter template. No tests, no linter configured. Site URL: https://adrian-altner.de.
Internationalisation (de default, en secondary)
- Astro i18n is configured in astro.config.mjs with
defaultLocale: 'de',locales: ['de', 'en'],prefixDefaultLocale: false. German lives at/, English at/en/. - Posts are organised per locale under
src/content/posts/<locale>/…. Postidis<locale>/<slug>; helpers in src/i18n/posts.ts (entryLocale,entrySlug,getPostsByLocale,getCategoriesByLocale,getPostsByCategory,categoryHref,categorySegment) split and filter them. The content schema in src/content.config.ts globs{de,en}/**/*.{md,mdx}. Collection name isposts— access viagetCollection('posts')/CollectionEntry<'posts'>. - A second collection
categorieslives undersrc/content/categories/<locale>/*.md(schema:name, optionaldescription, optionaltranslationKey). Blog posts reference a category viacategory: reference('categories')in the schema; frontmatter values are fully-qualified entry ids, e.g.category: de/technikorcategory: en/tech. Resolve withgetEntry(post.data.category). - Translation linking: both collections support an optional
translationKeystring. Entries in different locales that represent the same logical content share one key (e.g.de/technikanden/techboth settranslationKey: tech). The language switcher resolves this viafindTranslation(entry, target)(src/i18n/posts.ts) to produce the correct target-locale URL. Pages that render a specific content entry should pass it toHeaderasentry={…}(see src/pages/[...slug].astro, CategoryDetailPage); theBlogPostlayout forwards anentryprop. When an entry has no translation, the switcher falls back to the other locale's home rather than producing a 404. - Category routes are localised in the URL:
/kategorie/<slug>(de) and/en/category/<slug>(en), driven by src/pages/kategorie/[slug].astro and src/pages/en/category/[slug].astro. Both use shared UI components (CategoryIndexPage, CategoryDetailPage).categorySegment(locale)returns the right URL segment. - Because posts sit two levels deep, hero images and component imports inside MD/MDX use
../../../assets/…/../../../components/…relative paths. - UI strings and path helpers live in src/i18n/ui.ts.
t(locale, key)for translations;localizePath(path, locale)prefixes/enwhen needed;switchLocalePath(pathname, target)rewrites the current URL to the other locale (used by the header language switcher and hreflang alternates in BaseHead.astro). - Site titles/descriptions per locale live in src/consts.ts (
SITE.de,SITE.en). TheSITE[locale]map is the single source of truth — update when rebranding. - Pages: German under
src/pages/(index.astro,about.astro,[...slug].astro,rss.xml.js), English mirrors undersrc/pages/en/. The shared home UI lives in src/components/PostsList.astro; bothindex.astrofiles are thin wrappers that passlocale="de"/locale="en". - Layouts: BaseLayout.astro is the common skeleton (
<html>/<head>withBaseHead/<body>withHeader+main+Footer). Acceptstitle,description,locale, optionalimage, optionalentry(for the language-switch translation lookup), optionalbodyClass, and aheadnamed slot for per-page<link>/<meta>extras. All page templates compose via this layout — don't re-assemble head/header/footer by hand. Post.astro wrapsBaseLayoutto add hero image + title block + category line for single posts.Header,BaseHead, andFormattedDatealso acceptlocaledirectly and fall back togetLocaleFromUrl(Astro.url). - Separate RSS feeds per locale:
/rss.xml(de) and/en/rss.xml. The sitemap integration is configured withi18n: { defaultLocale: 'de', locales: { de: 'de-DE', en: 'en-US' } }.
Routing and data flow
- src/pages/[...slug].astro and src/pages/en/[...slug].astro generate post pages via
getStaticPaths+getPostsByLocale(locale), rendering through BlogPost.astro. @astrojs/sitemapgenerates the sitemap index;<link rel="alternate" hreflang="…">tags are emitted inBaseHead.astrofor both locales plusx-default.- Fonts: Atkinson is loaded as a local font via Astro's
fontsAPI inastro.config.mjs, exposed as CSS variable--font-atkinson. Files insrc/assets/fonts/. - MDX is enabled via
@astrojs/mdx; posts can mix Markdown and components.
Hero image workflow
Per user convention: hero image templates live under src/assets/heros/*. Workflow uses Maple Mono font, headless Brave to render, then sharp to export JPG at 1020×510.