This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is the Upstream Labs website, built on the AstroWind template - an Astro 5 site using Tailwind CSS. Upstream Labs is devoted to building sustainable open-source ecosystems through training, mentorship, and funding.
- Upstream 2025 (源起之道) - Flagship training program: https://camp.upstreamlabs.org
- GOBI Conference - Global Open Source Business Innovation: https://gobi.upstreamlabs.org
- X (Twitter): https://x.com/UpstreamLabs_X
npm run dev # Start development server
npm run build # Production build
npm run preview # Preview production build
npm run check # Run all checks (astro, eslint, prettier)
npm run check:astro # TypeScript/Astro validation only
npm run check:eslint # Lint only
npm run fix # Auto-fix eslint and prettier issuesThe site uses a YAML-based configuration loaded via a custom Astro integration:
src/config.yaml- Main site config (SEO metadata, analytics, theme)vendor/integration/- Custom Astro integration that:- Reads
src/config.yamland exposes it as a virtual moduleastrowind:config - Provides
SITE,METADATA,UI,ANALYTICSexports - Auto-updates
robots.txtwith sitemap URL on build
- Reads
Use ~/ to import from src/:
import { something } from '~/utils/permalinks';- English locale:
/en/prefix - Chinese locale:
/cn/prefix - Pages are duplicated in each locale folder with translated content
- Navigation is locale-aware via
src/navigation.ts
Pages (in both /en/ and /cn/):
index.astro- Homepageabout.astro- About Upstream Labscontact.astro- Contact informationfaq.astro- Frequently asked questionsprivacy.astro- Privacy policyterms.astro- Terms of serviceservices.astro- Programs page (orphaned, not in navigation)
src/components/widgets/- Page section components (Hero, Features, FeaturesCards, Footer, etc.)src/components/ui/- Reusable UI primitivessrc/components/common/- Shared componentssrc/layouts/- Page layouts (Layout, PageLayout, LandingLayout)
src/pages/en/- English locale pagessrc/pages/cn/- Chinese locale pagessrc/navigation.ts- Header and footer navigation configuration (locale-aware)
Custom theme variables via CSS custom properties:
- Colors:
azure,aqua,gold,ink(brand colors) - Section backgrounds:
section-bg-azure-glow,section-bg-gold-accent - Gradients:
hero-gradient,cta-gradient - Dark mode: class-based (
darkMode: 'class')
This site uses Astro's View Transitions (ClientRouter). This affects how scripts work:
When users navigate between pages, the DOM is swapped but inline scripts may not re-run. Event listeners attached to elements become orphaned.
For any script that attaches event listeners to DOM elements:
<script is:inline>
// ONE-TIME SETUP (runs once, persists across navigations)
(function() {
if (window._myFeatureInitialized) return;
window._myFeatureInitialized = true;
// Global event listeners (scroll, keydown, resize, etc.)
window.addEventListener('scroll', handleScroll, { passive: true });
document.addEventListener('keydown', handleKeydown);
})();
// PER-PAGE SETUP (runs on each page load/navigation)
function initMyFeature() {
const element = document.querySelector('.my-element');
if (element) {
// Use onclick/onchange instead of addEventListener to avoid duplicates
element.onclick = () => { /* handler */ };
}
}
// Run on initial load
initMyFeature();
// Re-run after View Transitions
document.addEventListener('astro:after-swap', initMyFeature);
</script>- Use
onclick/onchangeproperties instead ofaddEventListenerfor element-specific handlers (avoids stacking listeners) - Always handle
astro:after-swapfor any script that needs to run on every page - Use a guard flag (
window._featureInitialized) for one-time global listeners - Query elements fresh in the per-page function (don't cache references at top level)
BasicScripts.astro- Handles theme toggle, scroll behavior (useswindow.onload+astro:after-swap)HeaderNew.astro- Handles mobile nav, scroll detection (uses the pattern above)
- Astro scopes
<style>blocks to the component usingdata-astro-cid-*attributes - Child component elements won't match scoped selectors - use
:global(.class)to style them - Example: Styling a button from
ToggleTheme.astroinsideHeaderNew.astrorequires:global(.theme-toggle-btn)
- Header (
.main-nav-bar):z-index: 40 - Hero sections:
z-index: 0(usez-0class) - Overlays that shouldn't block clicks: add
pointer-events: none
- Hero components need
pt-10on mobile to clear the fixed header - Desktop uses
md:pt-[76px]ormd:pt-[120px]depending on hero type - Always test navigation and interactive elements on mobile viewport
- Use local images from
src/assets/images/instead of external URLs (Unsplash, etc.) - Import images in frontmatter:
import heroImage from '~/assets/images/hero.jpg' - Use consistent border-radius:
rounded-2xlfor hero/content images
- Theme toggle saves to
localStorage.theme - Use
:global(.dark)prefix for dark mode CSS in scoped styles - Test both light and dark modes when making visual changes