The dashboard
Every screen the CMS ships, what each one derives its numbers from, and the one rule that keeps a count from disagreeing with the list beneath it.
The CMS is thirteen routes under /admin/, plus two endpoints, all rendered on demand and all behind the session guard. The sidebar is generated from your configuration rather than written by hand, so adding a collection to src/admin.config.ts adds its screen and its nav row together.
The map
| Route | Screen |
|---|---|
/admin/ |
Overview — deploy state, the review queue, the working tree, recent activity |
/admin/<collection>/ |
One list per configured collection |
/admin/editor/<collection>/<slug>/ |
The entry editor |
/admin/pages/ |
The page composer’s index |
/admin/pages/<slug>/ |
Composing one page |
/admin/images/ |
The image library |
/admin/review/ |
Review and publish |
/admin/team/ |
Team, roles and invites |
/admin/settings/ |
Settings |
/admin/signin/, /admin/signup/ |
Auth (public) |
/admin/invite/<token>/, /admin/verify/<token>/ |
Token redemption (public) |
The sidebar groups them as Overview, then Sections (one row per collection, plus Pages and Team), then System (Images, Settings). Every collection row carries a badge counting what is waiting on you in that collection.
One clock, one read
The rule that shapes every screen: a page reads its state once and hands it down, rather than each panel reading for itself.
The Overview says “5 files changed” in a strip and lists those five in the panel beside it. Two reads a few milliseconds apart, with somebody saving between them, would render a count and a list that disagree — so the route reads once and passes both a value and the instant it was measured at. The same applies to relative dates: one now per render, so every “2 hours ago” on a screen agrees with every other one.
Nothing is cached between requests, deliberately. A stale deploy card that lies about whether your save landed is worse than a spawn.
Overview
Four panels, all reads:
- Deploy strip — what the site’s state is, and what is waiting.
- Queue panel — the pending changes, workspace-wide.
- Working tree — what has changed on disk but is not committed.
- Activity feed — recent commits, read from
git log.
The “N waiting on you” figure at the top is the workspace total, summed from the same function each sidebar badge uses at collection scope. The badges therefore cannot disagree with it.
The status pill
Top-right on the editor, the review screen and the Overview. It has four resting states and moves through them in order:
dirty → awaiting publish → building → live
The last transition is real rather than optimistic: after a publish the screen polls your production site’s build stamp and only says live once the deployed commit is an ancestor of what was pushed. If it takes too long it says so in a sentence rather than spinning forever.
The pill is invisible below the
lgbreakpoint. On a narrow window a save or a publish gives no visual feedback, which is a known gap — the work still happens.
Below sm
The 240px sidebar becomes a drawer, opened from a trigger in the top bar. Every admin route measures zero horizontal and vertical overflow at 375, 640 and 1024 pixels.
Dark, and only dark
The dashboard has one theme. There is no light mode and no dark: variant to pair against — the theme toggle in the top bar switches your site’s preview, not the CMS chrome.
The dashboard’s language
Nineteen languages ship: English plus Arabic, German, Spanish, French, Hindi, Indonesian, Italian, Japanese, Korean, Norwegian Bokmål, Dutch, Polish, Brazilian Portuguese, Swedish, Thai, Turkish, Vietnamese and Simplified Chinese.
It is a deployment setting, not a per-account preference — Settings → Project → Language, and it changes the dashboard for everyone who signs in. Arabic sets dir="rtl", which buys the bidirectional text algorithm; the layout itself does not mirror, because the admin’s spacing utilities are physical rather than logical.
One known gap: the shared UI primitives are outside the translation scan, so the password field’s Show/Hide still reads in English in every language.
Troubleshooting
A relative date on a collection list looks frozen. It is. That one module builds its rows at module scope, so on a long-lived server the “2 hours ago” on a list is measured from boot rather than from the request. Harmless in dev, which restarts constantly. The review screen sidesteps it by dating a change from its own commit.
A new screen 404s under astro dev. The route table is scanned at config load, not per request. Restart the dev server. Editing an existing route still hot-reloads.
Two of the four status filters are always empty. Ready and Needs review describe where an edit sits in git. Ready fills once you have uncommitted or unpushed work; Needs review is reserved for a pull-request flow that is post-v1. They are drawn because the design draws them, and they are not faked from a field that means something else.