Installation
TVfolio ships as a complete Astro project, not as an npm package or an integration you register. You clone the repository, install its dependencies and start editing — there is no theme layer between you and the markup, and nothing is hidden inside node_modules. Every component, token, icon, animation and the PDF writer that renders the CV are files in src/ that you own from the first commit.
It runs with no configuration, no API keys and no accounts. pnpm install followed by pnpm dev gives you the whole site — the home page, a six-project work archive, an eight-post blog, about, a CV that also downloads as PDF and plain text, contact, two legal pages and a no-signal 404 — populated with written sample content. The contact form is the only thing that wants a key, and it says so on screen rather than failing silently when it has none.
Requirements
- Node.js 22.13.0 or newer.
engines.nodeinpackage.jsonpins the floor at>=22.13.0. The floor is set by pnpm 11 rather than by the language, but the test runner also depends on Node’s--experimental-strip-types, which every*.test.tsundersrc/runs through. Check yours withnode -v. - pnpm. The lockfile is
pnpm-lock.yamlandpnpm-workspace.yamlcarries settings only pnpm reads.corepack enable pnpmis the shortest route if you do not have it. - Git, to clone the repository and to keep your own history from the first commit onward.
You do not need a hosting account, an email provider or a domain to run the theme locally. One route — POST /api/contact/ — is server-rendered, and it renders perfectly well without an email provider configured; it logs a readable error and redirects the visitor to a “mail me directly” message instead of failing the build. Everything else is a prerendered file.
Get the code
Clone the repository and drop the upstream history so your project starts with a clean log:
git clone https://github.com/Astro-Craft-Theme/tvfolio.git my-portfolio
cd my-portfolio
rm -rf .git && git init
If you would rather keep the upstream remote so you can pull theme updates later, skip the rm -rf .git and rename the remote instead:
git remote rename origin upstream
git remote add origin [email protected]:you/my-portfolio.git
Keeping upstream is only worth it if you plan to merge theme changes. Because the theme is source you edit directly, a merge after heavy customization is a real merge — most people take the clean-slate route and treat the clone as a starting point.
Install dependencies
pnpm install
The runtime dependency list is ten packages, and the shape of that list is the design philosophy of the theme in one place:
"dependencies": {
"@astrojs/cloudflare": "^14.2.5",
"@astrojs/mdx": "^7.0.0",
"@astrojs/sitemap": "^3.7.3",
"@fontsource-variable/inter": "^5.2.8",
"@tailwindcss/vite": "^4.3.2",
"astro": "^7.2.9",
"sharp": "^0.35.3",
"tailwind-merge": "^3.6.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.3.2"
}
Astro, Tailwind and its two class-composition helpers, sharp for build-time image optimization, one variable font, and three official Astro integrations — MDX, the sitemap and the Cloudflare adapter. There is no UI kit, no animation library, no SEO package, no icon package, no schema package, no RSS package, no PDF library and no CMS. Those are all implemented inside the theme rather than pulled in: the 40 UI primitives, the 89-utility motion catalog, the JSON-LD builders, the 572-icon registry, the hand-rolled RSS feed, the byte-exact PDF writer and the typed config layer are source files in src/. That is why the surface you have to keep up to date stays small, and it is also why customizing any of them means editing a file rather than reading a plugin’s options.
Start the dev server
pnpm dev
The site is at http://localhost:4321. Every route is available immediately with sample content in place:
/— the home page, the name in lights inside the tube/work/and/work/<slug>/— the transmission archive plus six project pages/blog/and/blog/<slug>/— the bulletin index plus eight posts/about/,/cv/,/contact//privacy/,/terms/— the two legal pages, rendered from typed config/404— the no-signal test card/examples/ui— the dev-only primitive catalog, which emits no paths in a production build
Alongside them, six generated endpoints: /robots.txt, /llms.txt, /rss.xml, /sitemap-index.xml, /cv.txt and /cv.pdf. All of them derive their absolute URLs from site, so setting that value once fixes them together.
What a production build actually emits
SITE_URL=https://example.com pnpm build
A clean build produces 23 HTML pages and 22 sitemap URLs — the 404 is excluded from the sitemap deliberately, since it is a state rather than a page. Counting the five generated text and binary endpoints alongside the HTML gives the 28 prerendered routes the adapter’s comment in astro.config.mjs refers to.
The Cloudflare adapter splits dist/ in a way that surprises people coming from Netlify or Vercel: pages and their assets land in dist/client/, and the Worker bundle in dist/server/. wrangler.jsonc points its static-asset binding at dist/client, which is load-bearing — pointing it at dist would publish the Worker bundle as public files. Deployment covers the rest.
The four things to change first
The theme is drawn around Alex Mercer, a demo persona, and the Figma frames are labelled with that persona’s facts. Replacing five fields in src/config/siteData.json.ts re-labels the whole set — the name in lights, the role line, the location, the availability specs and every page description derive from it.
What siteData cannot reach is prose, and the persona’s prose lives elsewhere. Before you deploy, work through src/config/cvData.json.ts (the whole career record, which feeds /cv/, /cv.txt, /cv.pdf and /about/), the three content collections under src/data/, src/config/legalData.json.ts, and the editorial sentences hard-written into src/pages/about.astro, contact.astro and index.astro. Configuration has the complete list with what each file owns.
Two settings in pnpm-workspace.yaml are worth knowing about before they surprise you. If your install prints a notice about ignored build scripts, that is allowBuilds working as designed — the packages it denies do not need to compile for the project to work. The Tailwind package set is version-pinned to what the theme was built and tested against, including the platform-specific @tailwindcss/oxide-* binaries.
Verify the install
pnpm lint && pnpm check && pnpm build && pnpm test && pnpm wiki:lint
That is the theme’s own gate, and it is worth running once before you start editing so you know the baseline is green. pnpm test runs every *.test.ts under src/ through Node’s type stripping — no framework, no fixtures — and there are 12 of them, all passing on a fresh clone. It fails if it finds none, so a check cannot go missing unnoticed. Commands & Testing explains what each gate actually catches.