A deep-space observatory interface for browsing NASA's universe data. Navigate between two views โ the Astronomy Picture of the Day and a live Asteroid Watch โ using a persistent bottom navigation bar.
Built with Svelte 5 (runes) and SvelteKit, Vite, and plain CSS custom properties.
๐ญ Live site: https://myapp-zeta-weld.vercel.app
Loads today's NASA Astronomy Picture of the Day automatically on open. Pick any date from 16 June 1995 onwards and click Receive Signal to load that day's image or video alongside its title, description, date, and copyright.
Loads Near Earth Objects approaching Earth today automatically on open. Asteroids are sorted by proximity to Earth. Click any card to expand it and see:
Potentially hazardous asteroids are flagged in red with a pulsing dot. The date picker accepts dates up to 2100-12-31 for predicted future approaches. A banner appears when a future date is selected.
| Framework | Svelte 5 โ runes ($state, $derived, $props, $bindable) |
| App framework | SvelteKit |
| Build tool | Vite |
| Styling | CSS custom properties โ all tokens in src/lib/styles/tokens.css |
| Fonts | Space Mono ยท Cormorant Garamond via Google Fonts |
| APIs | NASA APOD API ยท NASA NeoWs API |
| Component tests | Vitest + Svelte Testing Library |
| E2E tests | Playwright (Chromium + Firefox) |
| Component explorer | Storybook 8 |
| CI | GitHub Actions |
| Deployment | Vercel |
git clone https://github.com/your-username/cosmos-observer-svelte.git
cd cosmos-observer-svelte
npm install
Create a .env file in the project root:
VITE_NASA_API_KEY=your_key_here
Get a free key at api.nasa.gov. Without one the app falls back to DEMO_KEY, which is rate-limited to 30 requests/hour per IP.
โ ๏ธ Never commit your
.envfile โ it is listed in.gitignore.
npm run dev
Opens at http://localhost:5173
| Script | What it does |
|---|---|
npm run dev |
Start Vite dev server |
npm run build |
Production build |
npm run preview |
Serve the production build locally |
npm run lint |
ESLint โ reports issues, no autofix |
npm run format:check |
Prettier โ checks formatting, no autofix |
npm run format |
Prettier โ fixes formatting in place |
npm run test |
Vitest โ single run, exits when done |
npm run test:watch |
Vitest โ watch mode for development |
npm run e2e test |
Playwright โ headless, Chromium + Firefox |
npm run e2e test -- --ui |
Playwright โ interactive UI mode |
npm run e2e test -- --headed |
Playwright โ visible browser window |
npm run storybook |
Storybook dev server on port 6006 |
npm run build-storybook |
Build static Storybook into storybook-static/ |
myapp/
โโโ src/
โ โโโ app.html # HTML shell
โ โโโ routes/
โ โ โโโ +layout.svelte # Root layout โ imports tokens.css
โ โ โโโ +page.svelte # Entry page โ renders App shell
โ โโโ lib/
โ โ โโโ api/
โ โ โ โโโ Apod.js # APOD API wrapper
โ โ โ โโโ Neo.js # NeoWs API wrapper
โ โ โโโ components/
โ โ โ โโโ Apod/
โ โ โ โ โโโ ApodHeader.svelte # Heading + ApodVisual
โ โ โ โ โโโ ApodVisual.svelte # CSS orbiting particle animation
โ โ โ โ โโโ ApodDatePicker.svelte # Date input + Receive Signal button
โ โ โ โ โโโ ApodImage.svelte # Renders <img> or <iframe>
โ โ โ โ โโโ ApodCard.svelte # Full result card
โ โ โ โโโ Neo/
โ โ โ โโโ NeoHeader.svelte # Heading + NeoOrbitVisual
โ โ โ โโโ NeoOrbitVisual.svelte # Animated orbital rings
โ โ โ โโโ NeoList.svelte # Count summary + list of NeoCards
โ โ โ โโโ NeoCard.svelte # Expandable asteroid detail card
โ โ โโโ styles/
โ โ โ โโโ tokens.css # All CSS custom properties
โ โ โโโ views/
โ โ โโโ ApodView.svelte # APOD page โ auto-loads today on mount
โ โ โโโ Neoview.svelte # NEO page โ auto-loads today on mount
โ โโโ stories/
โ โโโ Cosmos.stories.js # Storybook stories
โโโ tests/
โ โโโ setup.js # Vitest global setup
โ โโโ Apod.test.js # Component tests โ all APOD components
โ โโโ Neo.test.js # Component tests โ all NEO components
โโโ e2e/
โ โโโ Cosmos.spec.js # Playwright e2e tests
โโโ .github/workflows/
โ โโโ ci.yml # CI: lint + format + test + e2e
โโโ playwright.config.js
โโโ vitest.config.js
โโโ vite.config.js
โโโ svelte.config.js
This project uses Svelte 5's rune-based reactivity rather than the legacy Svelte 4 API. Key patterns used throughout:
// Reactive state
let loading = $state(false);
// Derived value
let hazardousCount = $derived(neos.filter((n) => n.is_potentially_hazardous_asteroid).length);
// Props โ including bindable for two-way binding
let { value = $bindable(''), loading = false, maxDate, onFetch } = $props();
Event handlers use the HTML attribute syntax (onclick, oninput) rather than Svelte 4's directive syntax (on:click, on:input).
Every pull request to main triggers two parallel GitHub Actions jobs:
Lint, Format and Test โ runs ESLint, Prettier check, and Vitest in sequence. All three must pass.
E2E Tests โ installs Playwright browsers, starts the Vite dev server automatically via the webServer config in playwright.config.js, then runs the full suite against Chromium and Firefox. The HTML report is uploaded as a workflow artifact on every run.
Merging to main triggers an automatic deployment to Vercel.