The Astro-based Galaxy Community Hub at galaxyproject.org. Content lives in /content/ at the repo root and is preprocessed into src/content/ (gitignored) during build.
cd astro
npm install
npm run dev # http://localhost:4321The dev command preprocesses content, generates redirects and search index, then starts the dev server with hot reload.
npm run build # full production build
npm run preview # serve the built site locallynpm run lint && npm run format:check # check
npm run lint:fix && npm run format # fix
npm run content:lint # check content normalizationnpm run test # Playwright E2E (starts dev server automatically)
npm run test:ui # Playwright with interactive UI
npm run test:unit # Vitest unit tests
npm run test:unit:watch # Vitest in watch modeContent files are plain markdown by default. Files that use interactive components (like <Icon>, <VegaEmbed>, <Twitter>, etc.) must opt in to MDX processing by adding components: true to their frontmatter:
---
title: My Page
components: true
---Without this flag, component tags will not be rendered. The npm run content:lint command (also part of --check mode in normalize-content) will flag files where the flag is out of sync with actual component usage.
Icon, VegaEmbed, Twitter, Mastodon, VideoPlayer, Carousel, Flickr, Supporters, Contacts, MarkdownEmbed, CalendarEmbed, Insert.
Since MDX files are parsed as JSX, a few things that work in plain markdown will break in MDX:
- HTML comments: Use
{/* comment */}instead of<!-- comment --> - Bare
<>: Use<>instead - Angle brackets before numbers/dashes: e.g.
<500or<---need escaping
These only matter in files with components: true. Plain markdown files are unaffected.
Common frontmatter fields for content pages:
---
title: "Page Title"
date: 2024-01-15
authors: [alice, bob]
tags: [tools, workflows]
autotoc: false # opt out of table of contents (default: true)
components: true # enable MDX component rendering
---A table of contents sidebar is automatically generated for any page with 2+ headings (h2–h4). To disable it on a specific page, set autotoc: false in frontmatter.
Use ## (h2) as the top-level heading in content — the page title is rendered as h1 by the layout. During preprocessing, pages with multiple # (h1) headings are automatically shifted down one level (h1→h2, h2→h3, etc.) to maintain a proper hierarchy.
Links in prose content have a subtle underline by default (40% opacity) that becomes fully visible on hover. This applies within .prose areas only — navigation, buttons, and heading anchors are unaffected.
Content preprocessing (src/build/preprocess.mjs) reads from /content/ and writes to src/content/:
- Files are sorted into collections (articles, events, news, platforms, bare-articles, inserts)
- Legacy syntax is converted (kramdown attributes, old Gridsome tags)
- Slugs are normalized (camelCase split, underscore→hyphen, lowercase)
- Images are copied to
public/images/{slug}/and paths rewritten - Files with
components: truebecome.mdx; everything else stays.md
astro/
├── src/
│ ├── build/ # Preprocessing, normalization, search index
│ ├── components/ # Vue 3 and Astro components
│ │ └── mdx/ # Components available in MDX content
│ ├── layouts/ # Page layouts
│ ├── pages/ # File-based routing
│ ├── styles/ # Global CSS (Tailwind 4)
│ └── content.config.ts
├── public/ # Static assets (images copied here during build)
└── tests/ # Playwright E2E tests