{"_id":"@dewsoft/landing-kit","_rev":"5-a1ec4d4ea6e3ef32bcff0f58f59d3e70","name":"@dewsoft/landing-kit","dist-tags":{"latest":"0.3.1"},"versions":{"0.1.0":{"name":"@dewsoft/landing-kit","version":"0.1.0","license":"MIT","_id":"@dewsoft/landing-kit@0.1.0","maintainers":[{"name":"tenggis","email":"diditrimorum72@gmail.com"}],"bin":{"landing-kit":"cli/index.mjs"},"dist":{"shasum":"cfc717bde26764f5df68a068f1006290236e1121","tarball":"https://registry.npmjs.org/@dewsoft/landing-kit/-/landing-kit-0.1.0.tgz","fileCount":85,"integrity":"sha512-2xLbbPZa6EbGWD1JsJN3Z/PnvvjJWvQsB8yHZEkm3x8Coz8L/BEY3Du2K42GySlvg5wVGAWXFAuldNUbzUItPA==","signatures":[{"sig":"MEUCIQCIHytBLhi+FZgAEwCQdn1CguBbvKX87q5vARMHwslNHQIgMZtODLRKOmByt7gWPiGf5Z9jNRSlHpNOLzyOFYOMKUA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":344588},"type":"module","gitHead":"5f801f7f63d5d55b45f7b2e346ef4d1e2ea8653f","scripts":{"dev":"vite dev","fix":"biome check --write .","lint":"biome ci .","build":"vite build","verify":"pnpm lint && pnpm typecheck && pnpm conventions && pnpm build && node scripts/verify-build.mjs","typecheck":"tsc --noEmit","lighthouse":"rm -rf .lighthouseci && pnpm build && lhci autorun","smoke:full":"KIT_CONFIG=default KIT_ANIMATION=on KIT_SUBMIT=server vite build && node scripts/verify-build.mjs","conventions":"node scripts/check-conventions.mjs","smoke:onepage":"KIT_CONFIG=onepage KIT_ANIMATION=off KIT_SUBMIT=endpoint vite build && node scripts/verify-build.mjs","lighthouse:desktop":"rm -rf .lighthouseci && pnpm build && lhci autorun --config=lighthouserc.desktop.json"},"_npmUser":{"name":"tenggis","email":"diditrimorum72@gmail.com"},"_npmVersion":"11.13.0","description":"A bilingual (Mongolian / English) landing-page boilerplate built on TanStack Start. The premise: a scaffolding CLI (or a person copying this repo) should be able to produce a different site by **swapping config files and env flags — never by editing compo","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.4.3","vite":"^7.3.6","react":"^19.2.8","motion":"^12.43.0","@lhci/cli":"^0.15.1","react-dom":"^19.2.8","typescript":"6.0.3","@types/node":"^24.13.3","tailwindcss":"^4.3.3","@types/react":"^19.2.18","@biomejs/biome":"^2.5.8","react-hook-form":"^7.85.0","@types/react-dom":"^19.2.4","@tailwindcss/vite":"^4.3.3","@vitejs/plugin-react":"^5.2.0","@tanstack/react-start":"^1.168.42","@tanstack/react-router":"^1.170.25","@fontsource-variable/inter":"^5.3.0","@fontsource-variable/manrope":"^5.3.0"},"_npmOperationalInternal":{"tmp":"tmp/landing-kit_0.1.0_1787023463796_0.6723707319872996","host":"s3://npm-registry-packages-npm-production"},"deprecated":"moved to @tanasoftllc/landing-kit"},"0.2.0":{"name":"@dewsoft/landing-kit","version":"0.2.0","keywords":["landing-page","scaffold","boilerplate","bilingual","mongolian","i18n","seo","tanstack-start","react"],"author":{"name":"Tanasoft"},"license":"MIT","_id":"@dewsoft/landing-kit@0.2.0","maintainers":[{"name":"tenggis","email":"diditrimorum72@gmail.com"}],"homepage":"https://github.com/tanasoft1/landing_kit#readme","bugs":{"url":"https://github.com/tanasoft1/landing_kit/issues"},"bin":{"landing-kit":"cli/index.mjs"},"dist":{"shasum":"2de9708453ad5a83c7deef93f64e9c856b1b6be6","tarball":"https://registry.npmjs.org/@dewsoft/landing-kit/-/landing-kit-0.2.0.tgz","fileCount":85,"integrity":"sha512-QJEOf9xhQDQh1g0hRyzqzRX7BpPOKjDea3YkrG+IMCD4NmPK4wsdn+0OIfEzhs9zGjqgYzbjgNgL54uA7H6vYg==","signatures":[{"sig":"MEYCIQDU4UbWIb84xshTQDdoK3rDsxWsq6bZfF0fMUJxc5YJhQIhAPlVWa6hDsXHUKyVJn1yMhPMhrkUKJMj2c52LfFYJvZd","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":333587},"type":"module","engines":{"node":"^20.19.0 || >=22.12.0"},"gitHead":"3a760ee38e53e4dc66312c4e91d22d78411985f8","scripts":{"dev":"vite dev","fix":"biome check --write .","lint":"biome ci .","build":"vite build","verify":"pnpm lint && pnpm typecheck && pnpm conventions && pnpm build && node scripts/verify-build.mjs","typecheck":"tsc --noEmit","conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tenggis","email":"diditrimorum72@gmail.com"},"repository":{"url":"git+https://github.com/tanasoft1/landing_kit.git","type":"git"},"_npmVersion":"11.13.0","description":"Scaffolds a bilingual (Mongolian + English) landing site: prerendered static HTML, per-locale SEO, and swappable blocks.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.4.3","vite":"^8.2.1","react":"^19.2.8","motion":"^13.1.0","@lhci/cli":"^0.15.1","react-dom":"^19.2.8","typescript":"6.0.3","@types/node":"^26.2.0","tailwindcss":"^4.3.3","@types/react":"^19.2.18","@biomejs/biome":"^2.5.8","react-hook-form":"^7.85.0","@types/react-dom":"^19.2.4","@tailwindcss/vite":"^4.3.3","@vitejs/plugin-react":"^6.0.5","@tanstack/react-start":"^1.168.46","@tanstack/react-router":"^1.170.29","@fontsource-variable/inter":"^5.3.0","@fontsource-variable/manrope":"^5.3.0"},"_npmOperationalInternal":{"tmp":"tmp/landing-kit_0.2.0_1787036232430_0.4858989175256587","host":"s3://npm-registry-packages-npm-production"},"deprecated":"moved to @tanasoftllc/landing-kit"},"0.3.0":{"name":"@dewsoft/landing-kit","version":"0.3.0","keywords":["landing-page","scaffold","boilerplate","bilingual","mongolian","i18n","seo","tanstack-start","react"],"author":{"name":"Tanasoft"},"license":"MIT","_id":"@dewsoft/landing-kit@0.3.0","maintainers":[{"name":"tenggis","email":"diditrimorum72@gmail.com"}],"homepage":"https://github.com/tanasoft1/landing_kit#readme","bugs":{"url":"https://github.com/tanasoft1/landing_kit/issues"},"bin":{"landing-kit":"cli/index.mjs"},"dist":{"shasum":"b472066a42e50cf486533e3a96161990e147e58b","tarball":"https://registry.npmjs.org/@dewsoft/landing-kit/-/landing-kit-0.3.0.tgz","fileCount":81,"integrity":"sha512-dIY8ZUIf6eAeh8t1lCWxHlzGLnNPPyh9U8EypbOMZ1MMREFmQgX7mucszTweEFo6pPK6ccXi2rty+5FfHyDCeA==","signatures":[{"sig":"MEUCIQCy4Ei+Ag+AivelLdo0cm+xOmsiVouqppfD7i/53X+oagIgcy1wI1cIwWemegl/L+pMGpE095KonTV1aOgrPlpGfHU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":340147},"type":"module","engines":{"node":"^20.19.0 || >=22.12.0"},"gitHead":"88a447cfd14f8a5fc8ebd4d7f18b8f4bf43de47d","scripts":{"dev":"vite dev","fix":"biome check --write .","lint":"biome ci .","build":"vite build","verify":"biome ci . && tsc --noEmit && node scripts/check-conventions.mjs && vite build && node scripts/verify-build.mjs","typecheck":"tsc --noEmit","conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tenggis","email":"diditrimorum72@gmail.com"},"repository":{"url":"git+https://github.com/tanasoft1/landing_kit.git","type":"git"},"_npmVersion":"11.13.0","description":"Scaffolds a bilingual (Mongolian + English) landing site: prerendered static HTML, per-locale SEO, and swappable blocks.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.4.3","vite":"^8.2.1","react":"^19.2.8","motion":"^13.1.0","@lhci/cli":"^0.15.1","react-dom":"^19.2.8","typescript":"6.0.3","@types/node":"^26.2.0","tailwindcss":"^4.3.3","@types/react":"^19.2.18","@biomejs/biome":"^2.5.8","react-hook-form":"^7.85.0","@types/react-dom":"^19.2.4","@tailwindcss/vite":"^4.3.3","@vitejs/plugin-react":"^6.0.5","@tanstack/react-start":"^1.168.46","@tanstack/react-router":"^1.170.29","@fontsource-variable/inter":"^5.3.0","@fontsource-variable/manrope":"^5.3.0"},"_npmOperationalInternal":{"tmp":"tmp/landing-kit_0.3.0_1787041529854_0.8559397119784848","host":"s3://npm-registry-packages-npm-production"},"deprecated":"moved to @tanasoftllc/landing-kit"},"0.3.1":{"name":"@dewsoft/landing-kit","version":"0.3.1","keywords":["landing-page","scaffold","boilerplate","bilingual","mongolian","i18n","seo","tanstack-start","react"],"author":{"name":"Tanasoft"},"license":"MIT","_id":"@dewsoft/landing-kit@0.3.1","maintainers":[{"name":"tenggis","email":"diditrimorum72@gmail.com"}],"homepage":"https://github.com/tanasoft1/landing_kit#readme","bugs":{"url":"https://github.com/tanasoft1/landing_kit/issues"},"bin":{"landing-kit":"cli/index.mjs"},"dist":{"shasum":"7c37c633e38daceb6469a3bdcdecc99325f522ff","tarball":"https://registry.npmjs.org/@dewsoft/landing-kit/-/landing-kit-0.3.1.tgz","fileCount":82,"integrity":"sha512-FBuBLM1EYCWszzFG725noEXoYJOttfkTNla0ifJl6YYxVZ2dBONOgwNrnZKVXZwsNjMzT1g9L24+eKKH5x0byg==","signatures":[{"sig":"MEYCIQCZjwXklsJR4CVpxB+SUA7NNWmsxi5Bj706K/RrGDfOVAIhAKAtZCcS65u8i7MKH8Bzpd/fH1ruqsKr79bih8Te6VFO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":368706},"type":"module","engines":{"node":"^20.19.0 || >=22.12.0"},"gitHead":"add6f8361f43ec30c7720e6b36b0751f89c226b9","scripts":{"dev":"vite dev","fix":"biome check --write .","lint":"biome ci .","build":"vite build","verify":"biome ci . && tsc --noEmit && node scripts/check-conventions.mjs && vite build && node scripts/verify-build.mjs","typecheck":"tsc --noEmit","conventions":"node scripts/check-conventions.mjs"},"_npmUser":{"name":"tenggis","email":"diditrimorum72@gmail.com"},"repository":{"url":"git+https://github.com/tanasoft1/landing_kit.git","type":"git"},"_npmVersion":"11.13.0","description":"Scaffolds a bilingual (Mongolian + English) landing site: prerendered static HTML, per-locale SEO, and swappable blocks.","directories":{},"_nodeVersion":"24.14.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.4.3","vite":"^8.2.1","react":"^19.2.8","motion":"^13.1.0","@lhci/cli":"^0.15.1","react-dom":"^19.2.8","typescript":"6.0.3","@types/node":"^26.2.0","tailwindcss":"^4.3.3","@types/react":"^19.2.18","@biomejs/biome":"^2.5.8","react-hook-form":"^7.85.0","@types/react-dom":"^19.2.4","@tailwindcss/vite":"^4.3.3","@vitejs/plugin-react":"^6.0.5","@tanstack/react-start":"^1.168.46","@tanstack/react-router":"^1.170.29","@fontsource-variable/inter":"^5.3.0","@fontsource-variable/manrope":"^5.3.0"},"_npmOperationalInternal":{"tmp":"tmp/landing-kit_0.3.1_1787046164557_0.9068158510516173","host":"s3://npm-registry-packages-npm-production"},"deprecated":"moved to @tanasoftllc/landing-kit"}},"time":{"created":"2026-08-18T03:24:23.569Z","modified":"2026-08-25T02:57:23.686Z","0.1.0":"2026-08-18T03:24:23.958Z","0.2.0":"2026-08-18T06:57:12.573Z","0.3.0":"2026-08-18T08:25:29.994Z","0.3.1":"2026-08-18T09:42:44.819Z"},"bugs":{"url":"https://github.com/tanasoft1/landing_kit/issues"},"author":{"name":"Tanasoft"},"license":"MIT","homepage":"https://github.com/tanasoft1/landing_kit#readme","keywords":["landing-page","scaffold","boilerplate","bilingual","mongolian","i18n","seo","tanstack-start","react"],"repository":{"url":"git+https://github.com/tanasoft1/landing_kit.git","type":"git"},"description":"Scaffolds a bilingual (Mongolian + English) landing site: prerendered static HTML, per-locale SEO, and swappable blocks.","maintainers":[{"name":"tenggis","email":"diditrimorum72@gmail.com"}],"readme":"# Landing Kit\n\nA bilingual (Mongolian + English) landing page, prerendered to static HTML.\n\nYou build a site by **editing config and copy files — not by rewriting components**. Everything\nbelow is written to keep that true.\n\n## Contents\n\n- [Create your site](#create-your-site)\n- [Running it](#running-it)\n- [What you edit](#what-you-edit)\n- [Adding a page](#adding-a-page)\n- [Adding a block](#adding-a-block)\n- [Adding a variant](#adding-a-variant)\n- [Changing the design](#changing-the-design)\n- [The contact form](#the-contact-form)\n- [Fonts and Mongolian Cyrillic](#fonts-and-mongolian-cyrillic)\n- [`/docs`: the live reference](#docs-the-live-reference)\n- [Removing the /docs page](#removing-the-docs-page)\n- [Rules the build enforces](#rules-the-build-enforces)\n- [Gotchas](#gotchas)\n- [Scripts](#scripts)\n- [Scaffolding options](#scaffolding-options)\n- [Working on the kit itself](#working-on-the-kit-itself)\n\n## Create your site\n\nNothing to install first. Pick the line for your package manager and run it — `my-site` is the\nfolder it creates:\n\n```bash\npnpm dlx @dewsoft/landing-kit@latest my-site     # pnpm\nnpx --yes @dewsoft/landing-kit@latest my-site    # npm\nyarn dlx @dewsoft/landing-kit@latest my-site     # yarn 2+  (on yarn 1, use the npx line)\n```\n\nIt asks four short questions. Use the **arrow keys** to move and **Enter** to choose — each option\nexplains itself, so there is nothing to memorise. For the blocks question, **Space** toggles items\non and off.\n\nThat question also has an **add your own** row. Choose it, type a name like `pricing`, press Enter,\nand you get a real section of your own — as many as you like. Press Enter on an empty box when you\nare done:\n\n```\n? Blocks   (↑↓ move · Space toggle · Enter confirm)\n  [x] hero      The opening section\n  [x] features  What you offer\n  [x] cta       A call to action\n  [x] contact   Contact form\n  [x] pricing   yours — placeholder text to replace\n> +   add your own  a section the kit has no copy for (pricing, faq, …)\n```\n\nEach one arrives on the home page with placeholder text, ready for you to write. You still need at\nleast one of the four the kit ships.\n\nEvery question opens on a sensible default, so pressing Enter four times is a valid run. Add\n`--yes` to skip the questions entirely.\n\nThen start it:\n\n```bash\ncd my-site\npnpm install      # or: npm install / yarn install\npnpm dev          # or: npm run dev / yarn dev\n```\n\nOpen the address it prints. **Mongolian is at `/`, English at `/en`.**\n\nNow go to [What you edit](#what-you-edit).\n\n## Running it\n\nDay to day you need two commands:\n\n```bash\npnpm dev       # while you work\npnpm verify    # before you deploy — runs every check\n```\n\nAny package manager works. Swap `pnpm` for `npm run` or `yarn` throughout.\n\n> **Do this before your first deploy:** open `src/config/site.config.ts` and change `url` to your\n> real domain. `pnpm verify` refuses to pass until you do — a wrong domain does not show on the\n> page, but it quietly breaks every link Google reads.\n\n## What you edit\n\nTwo words explain the whole model:\n\n- A **block** is one section of a page — hero, features, cta, contact.\n- A **page** is just an ordered list of blocks.\n\n### You don't need to know TanStack Start\n\nThis is built on TanStack Start, but you do not have to learn it to build a site. It does three\njobs for you, all of them already wired up:\n\n1. Turns your pages into plain HTML files at build time, so Google can read them.\n2. Serves Mongolian at `/` and English at `/en` from one set of components.\n3. Puts the right `<title>`, description and social tags on every page.\n\nThe files that talk to the framework are `src/app/` and `src/routes/`. **You never open them.**\nEverything you actually edit is ordinary React and TypeScript.\n\n### Files you edit\n\nStart here. This is where nearly all real work happens:\n\n| You want to | Edit |\n|---|---|\n| Change the words on the page | `src/blocks/<block>/copy.ts` |\n| Add, remove or reorder sections on a page | `src/config/pages.config.ts` |\n| Change the site name, menu or contact details | `src/config/site.config.ts` |\n| Change colours, fonts, spacing or radius | `src/styles/presets/*.css` |\n| Change how a section *looks* | that block's `.tsx` file, e.g. `src/blocks/hero/hero-centered.tsx` |\n| Add a whole new kind of section | a new folder in `src/blocks/` — use the command in [Adding a block](#adding-a-block) |\n\n### Inside a block folder\n\nEvery block is one folder, and every folder holds the same four kinds of file. Using\n`src/blocks/hero/` as the example:\n\n| File | What it does | Edit it when |\n|---|---|---|\n| `copy.ts` | Holds all the text, in both languages. | Changing any wording — this is the one you open most |\n| `block.ts` | Describes the block: its id, which layouts it offers, and which layout is the default. | Adding a layout, or changing the default |\n| `variants.ts` | Connects each layout name to its component. | Adding a layout (one line) |\n| `hero-centered.tsx` | The actual markup for one layout. There is one of these per layout. | Changing how the section looks |\n\nSo the everyday file is `copy.ts`, and the everyday change is text. The other three you touch only\nwhen adding or restyling a layout.\n\n`block.ts` and `variants.ts` are split for one measured reason, explained in\n[Why `block.ts` and `variants.ts` are separate](#why-blockts-and-variantsts-are-separate). The\nshort version: **never import a component into `block.ts`.**\n\n### Files you don't touch\n\nNothing here needs editing to build a normal site. If you think you need to change one of these,\nit is worth asking whether there is a config-shaped way to do it instead:\n\n| Path | Why leave it alone |\n|---|---|\n| `src/app/` | The framework's entry points. Editing these breaks the build in confusing ways |\n| `src/routes/` | Four files, and four is enough forever. A new page is a config entry, **not** a new route file — see [Adding a page](#adding-a-page) |\n| `src/lib/` | SEO, sitemap, page resolution. Driven entirely by your two config files |\n| `src/styles/theme.css` | The design *system*. To restyle, edit a preset instead — see [Changing the design](#changing-the-design) |\n| `src/integrations/` | Motion, theme and form-submit wiring, picked when the project was created |\n| `scripts/` | The checks behind `pnpm verify` |\n\n`src/components/` sits in between: layout primitives (`Section`, `Container`), the header and the\nfooter. You will edit the header or footer eventually. Leave `layout/` alone — every block depends\non it, and the [rules](#rules-the-build-enforces) assume it is intact.\n\n## Adding a page\n\n```bash\npnpm dlx @dewsoft/landing-kit add-page about\n```\n\nThat is the whole command. It gives you a working `/about` page in both languages with placeholder\ntext, and `pnpm verify` passes straight away. Then you edit two things by hand:\n\n1. The **titles and descriptions** for both languages — they arrive as `About (mn)` / `About (en)`,\n   so you can see what to replace.\n2. The **`blocks` array** — which sections the page shows.\n\nBoth live in the entry the command just wrote, in `src/config/pages.config.ts`.\n\n> **Important: no new folder, and no new route file.** If you have used Next.js or similar, this is\n> the one thing that works differently here. A page is **one entry in\n> `src/config/pages.config.ts`** — nothing else. `src/routes/$.tsx` already catches every path and\n> looks it up in that list, which is why `src/routes/` stays at four files no matter how many pages\n> you add.\n\nYou can pass the values up front instead, if you already know them:\n\n```bash\npnpm dlx @dewsoft/landing-kit add-page about --blocks=features,cta \\\n  --title-mn=\"Бидний тухай\" --title-en=\"About us\"\n```\n\nOptional flags: `--path=/company` (defaults to `/<id>`), `--desc-mn=…`, `--desc-en=…`.\n\n> **Why `pnpm dlx` and not just `landing-kit`?** Your project does not depend on this kit — it is a\n> standalone app, which is the point. So there is no `landing-kit` command on your PATH, and typing\n> one gives `command not found`. `pnpm dlx` downloads the kit, runs it once, and forgets it.\n\nThis is what the entry looks like, if you would rather write it yourself:\n\n```ts\n{\n  id: 'about',\n  path: '/about',\n  blocks: ['features', 'cta'],\n  seo: {\n    mn: { title: 'Бидний тухай', description: 'Компанийн танилцуулга.' },\n    en: { title: 'About us', description: 'About our company.' },\n  },\n},\n```\n\nThat one entry produces both locales' URLs, the sitemap entries and the prerendered files.\n\n**The two languages must not share a title or a description.** The build rejects that as duplicate\ncontent, so give each language its own wording. (This is why the placeholders differ.)\n\nTo put the page in the menu, add `{ target: 'about' }` to `nav` in `site.config.ts`. The label is\nthat page's `seo.title` for the language being rendered.\n\n> `nav` targets and link `target`s take a page id or a block id. A block id becomes an anchor on\n> whichever page holds it. A target matching neither fails the build, so you never ship a dead link.\n\n## Adding a block\n\nA block is a new kind of section — testimonials, pricing, an FAQ. If you know you want one while\nyou are still creating the site, type its name at the blocks question and it is made for you, on\nthe home page, in one step — see [Create your site](#create-your-site).\n\nAfterwards, one command does the same thing:\n\n```bash\npnpm dlx @dewsoft/landing-kit add-block testimonials\n```\n\nUnlike a page, this **does** create a folder: `src/blocks/testimonials/`. It writes four files\nthere, makes all three registrations, and `pnpm verify` passes right away. Then you edit two things:\n\n1. The **copy**, in `src/blocks/testimonials/copy.ts` — both languages are in that one file.\n2. The **look**, in `src/blocks/testimonials/testimonials-simple.tsx`.\n\n> **You will not see it on the site yet.** A new block is registered but on no page. Add\n> `'testimonials'` to a page's `blocks` array in `src/config/pages.config.ts` to make it appear.\n> Until then it shows only on [`/docs`](#docs-the-live-reference).\n\nWant two layouts instead of one? Name them up front:\n\n```bash\npnpm dlx @dewsoft/landing-kit add-block pricing --variants=simple,detailed\n```\n\nThe default is a single variant named `simple`. Same `pnpm dlx` reason as\n[Adding a page](#adding-a-page) — there is no bare `landing-kit` command.\n\nBy hand, you write the four files described in\n[Inside a block folder](#inside-a-block-folder), then register the block in three places:\n`registry.ts`, `block-modules.ts` and `variants.all.ts`.\n\nGet a registration wrong and TypeScript names the missing one — all three are typed\n`Record<BlockId, …>`, and `BlockId` comes from the registry. This is the main reason to use the\ncommand instead.\n\n### Why `block.ts` and `variants.ts` are separate\n\nThis is the one part of the folder that is not obvious, and it is **not** a TanStack Start\nconvention — TanStack Start knows nothing about blocks. It is this kit's own design, for one\nmeasured reason.\n\n- **`block.ts`** holds the *facts*: id, variant names, the copy, the nav label. It is loaded\n  **immediately** for every block, because the `<title>`, the JSON-LD and the menu need each\n  block's text before anything renders.\n- **`variants.ts`** holds the *components*. They load **only for the blocks on the page you are\n  looking at**.\n\nThat is the whole point. Merging them into one file was measured:\n\n| | main bundle |\n|---|---|\n| two files (as shipped) | **316 KB** |\n| merged into one | **440 KB** |\n\nThe `motion` library stops getting its own chunk and joins the main bundle, so every visitor\ndownloads it whether the page animates or not. Nothing warns you: the build passes and every check\nstays green.\n\n**So the one rule is: never import a component into `block.ts`.** Everything else in the folder\nyou can rearrange freely.\n\n## Adding a variant\n\nA variant is a different layout for the same block and copy. Three steps, all inside the block's\nfolder:\n\n1. Add the component, e.g. `src/blocks/hero/hero-poster.tsx`.\n2. Add its name to `variantNames` in `block.ts` — `['centered', 'split', 'poster'] as const`.\n3. Map the name to the component in `variants.ts` — `poster: HeroPoster`.\n\nThen use it: `{ id: 'hero', variant: 'poster' }` in `pages.config.ts`.\n\nYou cannot forget step 3 — a name with no component is a compile error, and so is a component the\nnames never declare.\n\n## Changing the design\n\nAll colour, type and spacing lives in CSS variables, in two layers:\n\n- **`src/styles/theme.css`** — the system: type scale, spacing rhythm, and the mapping from\n  variables to Tailwind utilities. Don't edit this to reskin.\n- **`src/styles/presets/*.css`** — the skin: fonts, radius, shadows, and the light/dark palette.\n  This is what you replace.\n\nSwapping the whole look is one line:\n\n```css\n/* src/styles/theme.css */\n@import './presets/warm.css'; /* was './presets/editorial.css' */\n```\n\nTwo presets ship. `editorial.css` is quiet and neutral with a small radius and no card shadow.\n`warm.css` is amber and denser, with rounder corners and a real shadow. They differ in colour,\nradius, shadow and density on purpose, so a swap reads as a different design.\n\nTo add a third, copy a preset file and declare **the same variables**. That is checked, not\nadvisory: `pnpm conventions` fails if a preset misses a variable the theme maps. Without the check\nthe failure is invisible — a missing `--c-ring` just means no focus outline, and the build stays\ngreen.\n\nThe fastest way to see a preset is [`/docs`](#docs-the-live-reference), which renders every token\nlive.\n\n## The contact form\n\n`src/blocks/contact/contact-form.tsx`, built on `react-hook-form` and `zod`. The schema lives in\n`src/integrations/submit-schema.ts`.\n\nIt has two spam defences you should know about before you test it:\n\n- A **honeypot** field that must stay empty.\n- A **2 second minimum** on screen. Submit faster and it waits, it does not reject you.\n\n## Fonts and Mongolian Cyrillic\n\nBoth fonts **must** cover Cyrillic Extended. Mongolian uses `ө` and `ү`, which sit outside the\nbasic Cyrillic block.\n\nIf you swap a preset's fonts, check the replacement ships both a `cyrillic` **and** a\n`cyrillic-ext` subset. A font with only `cyrillic` renders those two letters in a mismatched\nfallback font — easy to miss if you only proofread the English.\n\nThis also means Mongolian pages load a little more font data than English ones. That is real and\nunavoidable, not a bug.\n\n## `/docs`: the live reference\n\n`/docs` is a generated, English-only page showing every design token, every block and variant, and\nthe resolved config. It reads the real code, so it cannot go out of date.\n\nVisitors never see it: it is not prerendered, not in the sitemap, and carries `noindex`.\n\n## Removing the /docs page\n\nIt costs visitors nothing, but delete it whenever you like:\n\n```bash\nrm src/routes/docs.tsx\nrm -rf src/components/docs\n```\n\nNothing else. `pnpm verify` passes with the route gone.\n\n## Rules the build enforces\n\nThese are checked automatically, so you will hear about them early. Each exists because breaking\nit fails silently.\n\n- **Spacing and width come from `<Section>` and `<Container>`.** Don't write raw padding, width,\n  `min-h-screen`, a bare `<section>`, or an inline `style` in a block.\n- **Never write a literal `<h1>` or `<h2>` in a block.** A block cannot know if it opens the page.\n  Use `const H = headingLevel === 1 ? 'h1' : 'h2'` and render `<H>`.\n- **Never import `Link` from `@tanstack/react-router`.** Every link is a plain `<a href>`. Blocks\n  are loaded once for the first URL, so a client-side jump would land on a page whose blocks were\n  never fetched. Full page loads are cheap here — every page is static HTML.\n- **Both locales, always.** There is no fallback language. `copy.ts` declares one type and types\n  both `mn` and `en` against it, so a missing translation is a compile error.\n- **No components in `block.ts`.** See [Adding a block](#adding-a-block).\n\n## Gotchas\n\n- **The contact form's 2 second guard also applies to you** while testing by hand. Working as\n  intended.\n- **`<input type=\"email\">` blocks submission before any handler runs**, so a malformed email never\n  reaches the schema. Don't hunt for a schema bug that isn't there.\n- **If the contact form \"silently fails\", check CORS first.** In endpoint mode the browser POSTs\n  cross-origin, so an endpoint without `Access-Control-Allow-Origin` fails at preflight — and it\n  surfaces as the same generic error a real code bug would.\n- **React warns `Invalid DOM property 'hreflang'` in dev.** Expected. The lowercase spelling is\n  what SEO tools read from the built HTML. Renaming it to `hrefLang` would ship the wrong casing.\n- **A light-only or dark-only build ships no theme-switching code at all** — none of the\n  JavaScript, not just a hidden toggle.\n- **A page flashing to \"Not Found\" right after it loads** usually means the host serves\n  prerendered files by their literal `index.html` filename. `normalizePath` in\n  `src/lib/pages/resolve-request.ts` handles this; check there first.\n\n## Scripts\n\n| Script | What it does |\n|---|---|\n| `pnpm dev` | Dev server |\n| `pnpm build` | Production build, including prerendering |\n| `pnpm typecheck` | `tsc --noEmit` |\n| `pnpm lint` | `biome ci .` |\n| `pnpm fix` | Auto-fixes what `lint` reports |\n| `pnpm conventions` | The rules above, plus preset and docs checks |\n| `pnpm verify` | Everything, in order. **Run this before you deploy.** |\n\n`pnpm verify` ends with `verify-build.mjs`, which reads the pages the build actually produced and\nchecks each one: exactly one `<h1>`, no hidden content in the HTML, a complete `hreflang` set,\nvalid JSON-LD, and a sitemap that agrees with the `<head>`.\n\n## Scaffolding options\n\nEvery question the CLI asks has a flag, so a scaffold can be fully scripted:\n\n```bash\npnpm dlx @dewsoft/landing-kit@latest frontend --yes\npnpm dlx @dewsoft/landing-kit@latest frontend --yes --add-blocks=pricing,faq\n```\n\nRun `pnpm dlx @dewsoft/landing-kit --help` for the full list. `--add-blocks` is the flag form of\nthe picker's **add your own** row, and takes as many names as you want.\n\nBlocks are not freely combinable: some blocks' copy links to others, and a link to a block you\nleft out would render a blank page. The CLI refuses those combinations at the question rather than\nletting the build fail later.\n\nThe generated project has no dependency on this kit. It is a plain TanStack Start app.\n\n## Working on the kit itself\n\nContributing to the kit, rather than building a site with it? The env flags, the second config, the\nLighthouse budgets and the publish steps are in [MAINTAINERS.md](./MAINTAINERS.md). None of it\napplies to a site you generate.\n","readmeFilename":"README.md"}