Architecture
The BEARS website is built with Astro.js 5.x, Tailwind CSS v4, and Alpine.js.
Technology Stack
- Astro.js 5.x — static site generation with file-based routing
- Tailwind CSS v4 — utility-first styling via Vite plugin
- Alpine.js — lightweight client-side interactivity
- TypeScript — strict mode enabled
- MDX — Markdown with component support for rich content pages
Project Structure
src/
├── pages/ # File-based routing (English, default locale)
│ └── de/ # German locale wrappers
├── layouts/ # Page layouts (BaseLayout, PostLayout, DocsLayout)
├── components/ # UI components organized by feature
├── content/ # Content collections (events, projects, docs, etc.)
│ # Localized collections use en/ and de/ subfolders
├── utils/ # Shared utilities (contentQueries, i18n, imageLoader, etc.)
├── styles/ # Global CSS and Tailwind theme
├── assets/ # Images and static assets (processed by Astro)
└── types/ # Shared TypeScript types
Content Collections
Content is managed through Astro’s content collections, defined in src/content/config.ts. Each collection has a Zod schema that validates frontmatter fields.
Localized collections (events, projects, page-text) use en/ and de/ subfolders. Language-neutral collections (sponsors, instagram, hero-slides, docs, people, testimonials, social-platforms) stay flat — people carries both role translations inline (roleEn/roleDe), and testimonials carries both quote translations inline (quoteEn/quoteDe).
A second class of collections — landing-section-visibility, about-section-visibility, sponsors-section-visibility, contact-section-visibility, media-section-visibility — store per-page section-toggle YAML (settings.yaml) rather than rendered content. Pages read them to skip entire sections at render time, and editors flip the toggles through the <page> — segments singletons in Keystatic.
Internationalization (i18n)
The site supports English (default, no URL prefix) and German (/de/ prefix). Configured via Astro’s built-in i18n in astro.config.mjs.
Key utilities in src/utils/i18n.ts:
getLocale(url)— derives locale from URL pathnamelocalizeUrl(path, locale)— adds/removes/de/prefixgetCategoryLabel(key, locale)— locale-aware category translationsgetHreflangUrls(pathname, origin)— generates SEO hreflang URLscatalogUiStrings— locale-aware UI labels for events/projects pages (filter, sort, status labels)mediaUiStrings— locale-aware UI labels for the media page
German pages in src/pages/de/ are thin wrappers that import and re-render the root page component. The root page detects its locale via getLocale(Astro.url) and passes it to all components and content queries.
Routing
Astro uses file-based routing. Files in src/pages/ map directly to URLs:
src/pages/index.astro→/src/pages/events.astro→/eventssrc/pages/events/[slug].astro→/events/:slugsrc/pages/de/events.astro→/de/events(German wrapper)src/pages/de/events/[slug].astro→/de/events/:slug