Marketing/brand website (Warden, a Techanv Consulting project). Built with Next.js (pages router), TinaCMS for content, and a WebGL/GSAP-driven frontend.
Runs on the latest major versions of the stack: Next 16, React 19, @react-three/fiber 9 / three 0.184 / drei 10, TinaCMS 3, Zustand 5.
| Tool | Version | Why |
|---|---|---|
| Node | ≥ 20.9 (.nvmrc → 22) |
Next 16 requires Node >=20.9. CI reads .nvmrc. |
| pnpm | 8 (pinned via packageManager) |
Repo lockfile is lockfileVersion 6.0 (pnpm 8). Do not use npm — it ignores pnpm.overrides and breaks dependency dedup. |
⚠️ This is a pnpm project. Usingnpm/yarnproduces duplicatescheduler/reactversions and a failed build.
# 1. Use the right Node version
nvm use # reads .nvmrc -> Node 22
# 2. Enable corepack so the pinned pnpm 8 is used automatically
corepack enable
# 3. Install dependencies
pnpm installNo environment variables are required for local development (see Environment).
pnpm devThis runs tinacms dev -c "next dev --webpack", which starts two things:
- the TinaCMS local GraphQL server on
http://localhost:4001/graphql - the Next.js app on
http://localhost:3000
| URL | What |
|---|---|
| http://localhost:3000 | The website |
| http://localhost:3000/admin/index.html | TinaCMS visual editor |
| http://localhost:4001/graphql | Content GraphQL API + playground |
❗ Do not run
next devon its own. Pages fetch content from the local Tina GraphQL server insidegetStaticProps. Withouttinacms devrunning, that server (localhost:4001) is down and you get:TypeError: fetch failed/Body must be a string. Always start the app withpnpm dev.
| Command | Description |
|---|---|
pnpm dev |
Tina GraphQL + Next.js dev server (use this for development). |
pnpm build |
Static export to ./out (see Build & deploy). |
pnpm preview |
Serve the built ./out locally on port 9992. |
pnpm lint |
ESLint (flat config, eslint.config.mjs). |
pnpm analyze |
Build with the bundle analyzer. |
The site builds to a fully static ./out directory (output: 'export') and is
deployed to GitHub Pages by .github/workflows/deploy.yml on every push to
main.
pnpm build # -> ./out
pnpm preview # serve ./out at http://localhost:9992pnpm build runs scripts/build.mjs, which:
- starts the Tina local GraphQL server (so
getStaticPropscan fetch content), then - runs
next build --webpackstandalone, then - shuts the server down.
⚠️ The build (andpnpm dev) is pinned to the webpack bundler via--webpack. Next 16 defaults to Turbopack, which cannot run this project's custom Sass functions (get/getColors/getThemesinnext.config.js) or the@svgr/webpack/ glsl / graphql loaders. Do not drop the--webpackflag.
⚠️ Do not change the build totinacms build -c "next build". That wrapper triggers a spurious<Html> should not be imported outside of pages/_documenterror during static export. Runningnext buildstandalone against the live Tina server (whatscripts/build.mjsdoes) is the working approach.
No Tina Cloud credentials are required to build — content comes from the local
markdown in tina/content/.
- Repo Settings → Pages → Build and deployment → Source: GitHub Actions.
- Custom domain
warden.techanv.comis set viapublic/CNAME. Add a DNSCNAMErecord:warden→techanvconsulting.github.io. - Not using a custom domain? Delete
public/CNAMEand setbasePath/assetPrefixto/wardeninnext.config.js(project pages are served from/<repo>).
Local dev works with no env vars — Tina runs in local mode against the
markdown content in tina/content/.
Editing through Tina Cloud and production builds need credentials (copy
.env.example to .env.local and fill in):
| Variable | Used for |
|---|---|
NEXT_PUBLIC_TINA_CLIENT_ID |
Tina Cloud client id |
TINA_TOKEN |
Tina Cloud read/write token |
TINA_SEARCH_TOKEN |
Tina search indexer token |
VERCEL_GIT_COMMIT_REF |
Branch Tina reads content from (defaults to main) |
Site content lives in tina/content/ as markdown/JSON:
tina/content/pages/home/home.md— homepage blocks (hero, features, etc.)- Schema (fields shown in the CMS) is defined in
tina/content/**/index.js.
The header navigation is not in the CMS: the anchor links
(Technologies, Solutions, Features, Tenets, Use Cases) are hard-coded in
components/header/index.js and components/header/mobile/index.js.
- Next.js (pages router) · TinaCMS (content + GraphQL)
- Three.js · @react-three/fiber · @react-three/drei · postprocessing
- GSAP · Lenis · Tempus · Theatre.js
- Scroll/RAF/measure hooks come from the maintained
lenis(lenis/react),tempus, andhamopackages (migrated off the deprecated@studio-freight/*).
- Scroll/RAF/measure hooks come from the maintained
- Zustand (state) · Embla Carousel
- Sass (CSS Modules) · @svgr/webpack (SVG imports, see
next.config.js) - next-sitemap (postbuild)
Standard Next.js layout (/public, /pages) plus:
- /assets — images, videos, SVGs
- /components — reusable components (each with its Sass module)
- /tina — TinaCMS config, content, schema, generated client
- /config — general settings
- /hooks — custom React hooks
- /layouts — top-level layout component
- /libs — scripts, WebGL, state store
- /styles — global styles and Sass partials
- ESLint flat config (
eslint.config.mjs, eslint 9) —next/core-web-vitalsrules + react-hooks, wired via each plugin's native flat config · Prettier (no semicolons, single quotes,endOfLine: auto) - Husky + lint-staged pre-commit hooks
| Symptom | Cause / Fix |
|---|---|
TypeError: fetch failed in getStaticProps |
Tina GraphQL server not running. Use pnpm dev, not next dev. |
Node.js version ">=20.9.0" is required |
Next 16 needs Node ≥ 20.9. Run nvm use (reads .nvmrc → Node 22). |
sassOptions.functions ... not supported when using Turbopack |
You dropped the --webpack flag. Build/dev must run on webpack (next build --webpack / next dev --webpack). |
Multiple versions of scheduler / react |
You used npm. Delete node_modules + package-lock.json, run pnpm install. Dedup is enforced via pnpm.overrides. |
better-sqlite3 build error (V8 / node-gyp) |
Wrong Node version. Run nvm use (Node 22). |
ERR_PNPM_LOCKFILE_BREAKING_CHANGE |
A newer pnpm rewrote the lock. Use pnpm 8 (corepack enable honors the pinned packageManager). |
Warning: __non_webpack_require__ is not defined |
Harmless — from Tina's local better-sqlite3 backend. Ignore. |