Template
84 lines
4.1 KiB
Markdown
84 lines
4.1 KiB
Markdown
# Stackship Circuit template
|
|
|
|
A React 19 + Vite starting point for apps that Circuit builds and Stackship runs. Routing,
|
|
light/dark theming, a typed API layer and the Stackship components are already wired, so an
|
|
agent (or you) starts with product work instead of plumbing.
|
|
|
|
It follows the same contract as Circuit's frontend template — dev server on `:5173`, mock API
|
|
at `/api`, `.eventsourced/session.yaml` for the live preview — with the Stackship design system
|
|
on top.
|
|
|
|
## Stack
|
|
|
|
- **React 19** with the React Compiler enabled
|
|
- **Vite 8** dev server, with a mock API served at `/api`
|
|
- **Tailwind v4** — Stackship tokens defined as CSS variables in `src/index.css`
|
|
- **Radix UI** (unified `radix-ui` package) for accessible primitives
|
|
- **SWR** for data fetching, **react-router** for routing, **lucide-react** for icons
|
|
- **Poppins + JetBrains Mono** bundled through Fontsource — no CDN calls, works air-gapped
|
|
|
|
## Structure
|
|
|
|
```
|
|
src/
|
|
api/ # typed API layer — one SWR hook per endpoint
|
|
types.ts # response types
|
|
client.ts # fetch wrapper (all network calls go through here)
|
|
hooks.ts # useStats(), useResources(), … consumed by pages
|
|
components/
|
|
ui/ # Stackship primitives: Button, Card, Badge, ServiceIcon, ProgressBar, Skeleton
|
|
gallery/ # component-gallery demos, one primitive per file
|
|
dashboard/ # dashboard sections (StatGrid, ResourcesTable, SentinelCard…)
|
|
home/ # home page sections (Hero, EnvironmentCard, LoopSection, StackGrid)
|
|
Layout.tsx # header + footer, wraps every route
|
|
Logo.tsx # Stackship wordmark, light/dark aware
|
|
ThemeProvider.tsx / ThemeToggle.tsx
|
|
pages/ # one file per route, registered in App.tsx
|
|
mock/server.mjs # zero-dependency mock API (started by the dev server)
|
|
public/ # favicon and Stackship logos
|
|
```
|
|
|
|
## Conventions (follow these when editing — applies to AI agents too)
|
|
|
|
- **Colours and fonts come from tokens only.** Use Tailwind classes like `bg-card`,
|
|
`text-muted-foreground`, `border-border`, `bg-primary`. Never hardcode hex values; both modes
|
|
read from the variables at the top of `src/index.css`. The one exception is the always-dark
|
|
hero surface, which uses `bg-night` and white text in both themes.
|
|
- **Brand utilities:** `text-gradient` (blue→green headline words), `bg-brand` (gradient CTAs
|
|
and meters), `glass`, `glow-primary` / `glow-accent` (blurred backdrops on dark sections).
|
|
Use the gradient sparingly — one highlight per screen reads as deliberate.
|
|
- **Resource types** get their accent from `ServiceIcon` (`app`, `database`, `storage`, …).
|
|
Don't invent new hues for them.
|
|
- **Sentinel** (the AI/ops assistant) uses the rose `sentinel` colour and nothing else does.
|
|
- **New route** = one file in `src/pages/` + one line in `src/App.tsx` (+ nav entry in
|
|
`Layout.tsx`).
|
|
- **New data** = type in `src/api/types.ts`, route in `mock/server.mjs`, hook in
|
|
`src/api/hooks.ts`. Pages never call `fetch` directly.
|
|
- **Loading** with `<Skeleton />`, **errors** inline with `<ErrorNote />` and a retry — see
|
|
`src/pages/Dashboard.tsx`.
|
|
- **Pages are compositions.** A page owns layout and data fetching, then renders sections from
|
|
`src/components/<page>/`. If a page file grows past ~100 lines, extract sections.
|
|
- **Copy:** sentence case for headings and buttons, no exclamation marks, no emoji. Calm and
|
|
declarative — "You control where data lives", not "Unleash your data".
|
|
- Radix provides behaviour, Tailwind provides looks. Copy styles from the gallery
|
|
(`src/pages/Components.tsx`) rather than inventing new patterns.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
npm run dev # app on :5173, mock API proxied at /api
|
|
npm run build # type-check + production build
|
|
npm run lint # oxlint
|
|
```
|
|
|
|
## Theming
|
|
|
|
Light and dark are two sets of CSS variables in `src/index.css` (`:root` and `.dark`). The
|
|
header toggle offers light, dark and system, and persists to `localStorage`. To retheme, change
|
|
the variable values — no component edits needed.
|
|
|
|
## Swapping in a real API
|
|
|
|
Point `server.proxy['/api']` in `vite.config.ts` at your backend and remove the `mockApi()`
|
|
plugin. Endpoint shapes stay in `src/api/types.ts`.
|