---
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. A whole site kept separate, byte for byte, is a site folder instead (/docs/file-storage). 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 or components, no path rewriting, no improvements nobody
asked for. A component only when the owner asks for one markup on many pages.

## 1. Is this your path?

| You have | Go |
|---|---|
| a folder, ZIP, repo or export that already renders in a browser, to become the shop's pages (live prices, cart, shop header) | **here** |
| a whole multi-page site to keep separate, byte for byte | a **site folder**, not this skill: category `type:"site"`, one ZIP to `POST /site-folders/{id}/releases/zip?base=none&publish=1`, publish the category — `/docs/file-storage`; served at the section's path, sandboxed (no cookies, no `localStorage`), no shop header, cart or `vz-` keys |
| not sure which of the two | ask the owner one question; do not choose for them |
| 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 outside guarded zones. The platform box is absent unless
  `props.html.boxed: true`. Check fixed anchoring and authored visibility
  side effects in `/docs/own-markup` §7.2 before promising the geometry.
- **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.
  `hidden` survives there, `data-*` does not.
- **Counters, sign-in, search suggestions need no script**: `vz-cart-count`,
  `vz-fav-count`, `vz-auth`, `vz-user-initial`, `vz-search`; events `vz:cart`,
  `vz:fav`, `vz:auth` on `document` (`/docs/own-markup` §4.0). Links open
  without a reload; `vz-reload` opts one out.
- **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 — then follow `mode_guide`: new pages beside working ones, rollback
   ready. 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: './'` (a site folder takes the whole export, §1).
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 widget folder — `page.extra` (a multi-page site kept whole is a
  site folder, §1); 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
