---
name: vizen-transfer
description: Move a FINISHED site or template — an HTML/CSS/JS folder, a static React/Vite export, a Figma/Webflow/Tilda export — into a Vizen shop through a personal token as it is: folder = level-3 widget in real page DOM, one command per folder, scripts and position:fixed work, no rebuild, no Shadow DOM. Layout folder, header/footer, pages, product/category templates, live-data re-binding, the check. Invoke when the task says transfer, migrate, "we already have a site", or hands you a folder, ZIP or repo.
---

# Transfer a finished site

You are **moving, not designing**. The owner's site defines the look; your job
is to make it appear in the shop as it is, re-bind the data the shop owns (menu,
prices, cart, forms) and prove the result with screenshots. No redesign, no
cutting into sections, no path rewriting, no improvements nobody asked for.

## 1. Is this your path?

| You have | Go |
|---|---|
| a folder, ZIP, repo or export that already renders in a browser | **here** |
| nothing yet — a page to build from scratch | `vizen-start` and its three questions |
| a question about vz- keys, partial releases, images inside one widget | `vizen-own-markup` |
| groups, bindings, system pages (cart, account…), sticky | `vizen-chrome` |
| goods, categories, filters | `vizen-catalog` |
| "200 and nothing changed" | `vizen-troubleshoot` |

The full contract — what the platform does to your file, the copy · remap · drop
table, framework notes — is `GET /docs/transfer`. Read it once, before step 2.

## 2. What the platform gives you (measured, not promised)

- **Folder = widget.** A level-3 html document is a folder: `index.html` at the
  root, css/js/assets in subfolders, relative paths from the root. A release
  uploads it; files are served from `/_html/{doc}/{rid}/…` with the same nesting.
- **Real page DOM.** No Shadow DOM, no iframe, no sanitiser: `<script>` and
  `<link>` run, `position: fixed`, `100vw` and `100dvh` resolve against the
  viewport, and there is no platform box around you (unless you ask for one
  with `props.html.boxed: true`).
- **Your `<body>` becomes** `<div class="vz-body vz-body-{doc}" data-vz-base="/_html/{doc}/{rid}/">`;
  `body`, `html` and `:root` selectors in your CSS are rewritten to that class;
  from `<head>` the platform keeps `link/style/script/noscript`, moves `title`
  and `meta description` into the page SEO and drops the rest. A whole document
  and clean markup are both accepted.
- **Scripts run on first parse and are re-run after in-site navigation.** Init
  immediately and again on `document`'s `vz:navigate` event; never wait for
  `DOMContentLoaded` alone. Paths built by script start from
  `document.currentScript?.closest('.vz-body')?.dataset.vzBase ?? window.VZ_ASSET_BASE ?? ''`
  (external or `defer` script: `document.querySelector('.vz-body-<id>')?.dataset.vzBase`).
- **The cascade is shared.** The storefront ships Tailwind preflight: `h1–h6`
  inherit size and weight, `ul/ol` lose bullets, `a` inherits colour and loses
  the underline, `*` has zero margin and padding, `img` is `display: block`,
  `button` is reset. Everything you styled explicitly survives; what you left to
  browser defaults is reset — ship your own base styles under your root class.
- **Guarded zones** (`/cart`, `/checkout`, `/account`, `/wishlist`, `/orders`,
  `/deals`, `/dashboard`): your markup renders, scripts do not run, and a
  widget's relative `<link rel="stylesheet">` is dropped with them — the
  header's CSS belongs in the layout folder (linked everywhere) or inline.
- **Live data is server-side substitution**: `{{ product.name }}`,
  `vz-for="p in products"` (the section's feed, ≤ 48 rows, no filters or paging),
  `vz-for="m in menu"`, `vz-add-to-cart`, `vz-cart-count`, `href="form:<id>"`.
  Names come from `GET /docs/vz-keys.json`, never from memory.

## 3. The order of work

1. **Passport.** `GET /v1/account/token`. `contour: "dev"` writes a draft the
   owner sees at `<slug>--dev.<zone>`; `"prod"` writes the live site on every
   release. Iterate on a dev key; if you hold a prod key, say so and ask for a
   dev one. Level 3 needs the owner or admin role on the shop.
2. **Inventory.** One table: source page or part → target (page widget, layout
   folder, chrome group, product or category template) → data to re-bind →
   what cannot move (SPA runtime, router, own fetch, server code, service
   worker). A client-rendered SPA is exported statically first: one route =
   one folder, `base: './'`.
3. **Layout folder** — site-wide CSS, fonts, favicon, scripts:
   `node backend-3D/tools/html-transfer/transfer.mjs ./theme --theme <id>`
   (the id: `GET /content-blocks`, rows with `group_type == "theme"`, the
   earliest is the shop's default). Verify: `GET /themes/<id>/site` answers
   `base` and `scope_class`; a page root carries `.vz-theme-{doc}`.
4. **Header and footer.** `transfer.mjs ./header --name "Header"` — without
   `--page` it creates the document and publishes the release, nothing is
   mounted; the output prints the document id. Then an html block
   (`kind:"html"`, `props.html:{html_document_id, level:3}`) → a group
   `group_type:"chrome"` with `props.widgets:[<block id>]` →
   `PUT /design/bindings/shop/0 {"slots":{"header":<G_h>,"footer":<G_f>}}`
   (one section: `/category/<id>`; one layout: `/theme/<id>`). Request bodies:
   `vizen-chrome` §2–3. Sticky header: `vz-sticky` on your root node; the
   header's CSS — in the layout folder or inline `<style>`, not a relative
   `<link>` (dropped on `/cart`). Verify: `GET /v1/storefronts/resolve?slug=<shop>`
   → `chrome`; open the home page, a product page and `/cart` — styled on all three.
5. **Pages.** The page must exist as `type:"page"` (`GET /categories`; create with
   `POST /categories {"item":{"name":"…","type":"page","is_published":true,"seo":{"slug":"…"}}}`).
   `transfer.mjs ./home --page <slug> --validate-only` → fix the report →
   `transfer.mjs ./home --page <slug>`. A rerun is a new release and uploads
   only changed files; `--doc <id> --rollback` returns the previous one.
   The page keeps the platform's breadcrumbs and heading above your widget;
   for 1:1 switch them off with `{"page":{"hideSystemBlock":true}}` on the
   `__page:top` zone block (`/docs/transfer` §4.5). Verify: open the `url` the
   command prints — nothing of the platform above your first section.
6. **Product and category templates.** `transfer.mjs ./product --name "Product template"`
   → html block → group `group_type:"product"` →
   `PUT /design/bindings/category_children/<root category id> {"slots":{"body":<G>}}`
   (the whole subtree; one product: `/product/<id>`). A category template is a
   group `category` in the slot `body`; keep the platform `productListing`
   inside it when filters and paging are needed. Verify: two product pages
   show one card each and the price equals `GET /products/{id}`.
7. **Re-bind data.** Menu → `vz-for="m in menu"`; price → `{{ p.price_text }}`;
   buy → `vz-add-to-cart="{{ p.id }}"`; wishlist → `vz-fav`; forms →
   `href="form:<id>"`; internal links → `/path` or `vz:page/<id>`. Put
   `vz-for` and `vz-if` on different nodes. Verify: `curl` the page and grep a
   real product name in the raw HTML; add an item to the cart, watch the counter.
8. **Check and report.** Open by direct URL **and** by a click from another page;
   screenshots at 1440 and 390; console without errors and without 404s under
   `/_html/`; `/cart` still shows your header; `--doc <id> --list` shows the
   release. Report: links, both screenshots, what was re-bound, what was dropped
   and why, the rollback command. Never print the token.

## 4. Never — only what the platform refuses or breaks

- paths that leave the folder (`../x`, absolute paths to your own files) —
  `ref.outside` / `ref.missing`; `http://` resources — `ref.http`; server
  executables — `file.forbidden`; `navigator.serviceWorker.register`; a second
  `.html` in one folder — `page.extra`; more than 500 files, an HTML file over
  2 MB, a file over 50 MB, a release over 60 MB;
- a prod key for iterations; content that exists only after a script runs (an
  empty SPA shell); prices or menus typed by hand where the shop has them; init
  on `DOMContentLoaded` alone; uploading files one by one through the storage
  API; level-1 documents for a transfer;
- printing the token; `git`; writing to cart, checkout or account flows.

## 5. Links

- `GET /docs/transfer` — the contract, copy · remap · drop, frameworks, silently ignored
- `vizen-chrome` — groups, bindings, system pages, sticky
- `vizen-own-markup` §2–3 and `GET /docs/vz-keys.json` — keys and scope fields
- `backend-3D/tools/html-transfer/README.md` — every flag; `examples/` — a starter kit
- `vizen-troubleshoot` — when the API said yes and the site says no
