# Deploy a static Astro site on Yeeted

A two-page palette contrast report built by **Astro** as plain HTML, with
**no Dockerfile**: Yeeted detects Astro from `package.json`, sees no server
adapter, runs `npm ci` from the lockfile and `npm run build` (`astro build`),
and serves `dist/` from nginx. The pages ship no JavaScript; every ratio is
computed once, at build time.

The math in `src/lib/contrast.js` is the same module the
[Vue](contrast-vue.md) and [Svelte](contrast-svelte.md)
examples run in the browser, so the black-on-white row reading `21.00:1`
proves the build ran it. This is Astro's static output only: adding
`@astrojs/node` turns the project into a server, which Yeeted deploys through
a different path that this example does not cover.

Tests: `npm test` (node:test on the contrast math) · `./smoke.sh https://...`
against a deployment.

## Run locally

Use Node 22.12 or newer. From `node/contrast-astro`:

```sh
npm ci
npm test
npm run dev
```

Open the URL Astro prints. Edit the `palette` list at the top of
`src/pages/index.astro` to report on other color pairs. `npm run build` writes
`dist/index.html` and `dist/about/index.html`; the deployed image serves the
whole `dist/` folder.

## Deploy and verify

Follow the [deployment walkthrough](getting-started.md). Deploy this
folder, including `package-lock.json`, `astro.config.mjs` and `yeeted.yaml`,
with no database. Do not upload `node_modules/`, `.astro/` or `dist/`; the
build runs on Yeeted. The manifest routes to port 80, where nginx listens.

```sh
sh smoke.sh "$PREVIEW_URL"
```

The script checks that the report page is served at `/` and carries ratios
and grades that only exist once the build has run, and that `/about/` serves
the second page from its own folder.

## What the site does not do

The report is fixed at build time: changing the palette means a new deploy.
The page shows when it was built, which makes a redeploy easy to confirm.
Unknown paths are answered with the report page rather than a 404, because
the generated nginx configuration falls back to `index.html` for every
single-page app and static generator alike.

[All examples](index.md) · [Deploy an example](getting-started.md)
