4.0 KiB
4.0 KiB
SoftVowels — project conventions
A monorepo with two apps:
apps/web Astro 5 (static site, Tailwind v4)
apps/cms Payload 3 (admin UI + REST/GraphQL)
Package manager
pnpm 9. From the repo root:
pnpm install # install both
pnpm dev # dev:web
pnpm dev:web # Astro only
pnpm dev:cms # Payload only
pnpm build # build:web
pnpm build:cms # build Payload
pnpm typecheck # Astro check
pnpm format # prettier
pnpm format:check # prettier --check
Source paths
apps/web/src/components/{layout,home,article,category,search,newsletter,ui}/— componentsapps/web/src/layouts/— page layoutsapps/web/src/lib/— Payload client, types, SEO, formattingapps/web/src/styles/—global.css(Tailwind v4@themetokens) andprose.cssapps/web/public/fonts/— dropGeist-Variable.woff2+GeistMono-Variable.woff2hereapps/cms/src/collections/— Payload collectionsapps/cms/src/hooks/— Payload hooks (e.g.revalidateAstro)
Code style
- All component files are
.astrosingle-file components. Frontmatter (between---) is TypeScript. - No comments in code unless explicitly requested.
- Tailwind v4 tokens live in CSS (
@themeinglobal.css). Do not create atailwind.config.*file. - Use
icon(astro-icon) with names likematerial-symbols:memory. Self-host icon font woff2 underpublic/icons/material-symbols-outlined.woff2and wire it via@font-faceinglobal.css(already declared; add the binary file when ready). - Prefer
border border-border-subtle rounded-mdover arbitrary values to stay on the 4px baseline. - For code blocks in MDX / Lexical output, the article body uses
prose-article(seeprose.css).
Lint / typecheck
pnpm typecheck # astro check
pnpm format:check
There is no separate ESLint run; the project's quality bar is enforced by astro check and Prettier.
Build outputs
apps/web/dist/is bind-mounted into thewebnginx container (./apps/web/dist:/usr/share/nginx/html:ro). The host-side webhook rewrites this folder on demand; the nginx container does not need to be rebuilt.apps/cms/dist/is built by Payload (Next.js) and runs as the CMS Node process.
Deploy (homeserver)
- Copy the repo to
/opt/softvowelson the server. cp .env.example .envand fill in the secrets.docker compose up -d --build.- In Nginx Proxy Manager add two proxied hosts:
softvowels.example→web:80(force SSL, HSTS)cms.softvowels.example→cms:3000(force SSL, HSTS)
- On the host, install the rebuild webhook:
Make sure
sudo cp scripts/softvowels-webhook.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now softvowels-webhookREBUILD_TOKENin/etc/systemd/system/softvowels-webhook.servicematchesREVALIDATE_TOKENin.env.
Revalidate flow
- Editor publishes in
/admin. - Payload
afterChangehook POSTs tohttp://host.docker.internal:4321/__revalidate?secret=…&collection=…&op=…. - The host-side Node webhook spawns
scripts/rebuild.sh, whichgit pulls and runspnpm --filter web build. - nginx container now serves the new
apps/web/dist/.
Self-hosting fonts
Drop these two files into apps/web/public/fonts/:
Geist-Variable.woff2(https://github.com/vercel/geist-font — renameGeistVF.woff2)GeistMono-Variable.woff2(renameGeistMonoVF.woff2)
The @font-face rules are already declared in global.css.
Environment
Root .env (used by docker compose):
POSTGRES_PASSWORD=
PAYLOAD_SECRET=
REVALIDATE_TOKEN=
WEB_HOSTNAME=
CMS_HOSTNAME=
apps/web/.env:
PUBLIC_SITE_URL=
PUBLIC_PAYLOAD_API=
PAYLOAD_API_INTERNAL=
REVALIDATE_TOKEN=
apps/cms/.env:
PAYLOAD_SECRET=
DATABASE_URI=
PAYLOAD_PUBLIC_SERVER_URL=
SERVER_URL=
CORS_ORIGINS=
ASTRO_REVALIDATE_URL=
ASTRO_REVALIDATE_TOKEN=