Build and styling

Build system

The Website builds in two parts: Vite (with the Vike plugin) produces the SSR bundle and hashed client assets, and esbuild produces the Worker bundle.

The build pipeline

npm run build runs these steps in order:

NODE_ENV=production tsx scripts/build-static.ts \
  && tsc -b \
  && NODE_ENV=production vike build \
  && tsx scripts/patch-server-entry.ts \
  && tsx scripts/copy-static.ts \
  && tsx scripts/build-worker.ts
Step Script What it does
1 build-static.ts Generates static inputs; loads .env if present
2 tsc -b TypeScript project build
3 vike build Builds the Vike SSR bundle + hashed client assets
4 patch-server-entry.ts Patches the generated Vike server entry
5 copy-static.ts Copies static files into dist
6 build-worker.ts Bundles src/worker.tsdist/worker.js (esbuild)

The output is dist/client/ (assets served via the ASSETS binding) and dist/worker.js (the Worker, main in wrangler.toml).

Vite configuration

Key options in vite.config.ts:

  • JSX runtime — always the production automatic runtime (jsxDev: false) to avoid dev-runtime issues in the Workers environment.
  • define — bakes __BASE_URL__, __API_URL__, __GTM_ID__, __GTAG_ID__, __NODE_ENV__, __SUBSCRIPTIONS_API_URL__, __RECAPTCHA_SITE_KEY__, __CHAT_API_KEY__, __SENTRY_DSN__ into the bundle (see Environment setup).
  • manualChunks — React + scheduler go to react-vendor, other node_modules to vendor; chunk filenames are content-hashed (assets/js/[name]-[hash].js).
  • cssCodeSplit: true and CSS Modules with camelCase locals and scoped class names.
  • assetsInlineLimit: 8192 and source maps only outside production.
  • .dev.vars is loaded into process.env when present, so local builds bake in local-only build-time secrets.

esbuild Worker bundle

scripts/build-worker.ts bundles the Worker:

await build({
  entryPoints: ["src/worker.ts"],
  bundle: true,
  outfile: "dist/worker.js",
  format: "esm",
  target: "es2022",
  platform: "neutral",
  conditions: ["worker", "browser"],
  minify: true,
  define: { "process.env.NODE_ENV": '"production"' },
  external: ["mermaid", "lodash-es", "dagre-d3-es", /* … */],
});

It also copies Vike's generated server entry (dist/server/entry.mjs) to dist/server-entry.js.

Dev and watch scripts

Command Script Purpose
npm run dev wrangler dev --env dev Dev server on port 3000
npm run watch scripts/watch.ts Rebuild on change (no server)
npm run watch:dev scripts/watch-dev.ts Rebuild + dev server + browser reload
npm run build:worker scripts/build-worker.ts Rebuild only the Worker
npm run optimize:images scripts/optimize-images.mjs Convert PNG/JPG to WebP (Sharp)

Styling

The project uses SCSS with the BEM (Block-Element-Modifier) naming convention. There is no utility-CSS framework; styling is hand-authored SCSS plus a small set of inlined critical styles.

BEM

.header { }            // Block
.header__logo { }      // Element
.header--sticky { }    // Modifier

File organization (src/styles/)

Directory / file Purpose
abstracts/ Variables, mixins, functions (_variables.scss, _mixins.scss, _functions.scss)
base/ Base HTML element styles
components/ Component-specific styles (one file per component)
layout/ Layout-level styles
pages/ Page-specific styles (page-name.page.scss)
global.scss Global styles and imports
main.scss Main SCSS entry point

Conventions

  • Import styles via the @/styles/ alias.
  • Use variables from abstracts/ for colors, spacing, and breakpoints — no magic values.
  • Component styles live in styles/components/ matching the component name; page styles in styles/pages/ using the page-name.page.scss pattern.
  • No inline styles — use BEM SCSS classes. The one deliberate exception is the critical CSS inlined into the SSR <head> (pages/+onRenderHtml.tsx) to avoid FOUC for above-the-fold header/nav/container.
  • SCSS is compiled with sass; Vite's additionalData prepends @charset "UTF-8";.

Note

The platform's shared swapps-design system informs the visual language (typography, colors, components). The internal reference lives in the pages/style-guide/ route, which documents typography, colors, logo, icons, illustrations, and component patterns.

Quality gates

Command Purpose
npm run lint / lint:fix ESLint (flat config)
npm run format / format:check Prettier
npm run type-check tsc --noEmit
npm run test Vitest

lint-staged runs ESLint + Prettier on staged files via a Husky pre-commit hook; CI re-runs lint, format check, type check, build, and route tests (see Deployment).

Next steps