docs
/
AI skills

A page with several screens

What a screen is (data.screens), which one visitors see and how ?screen= shows the others, why screens beat data.html, what the first, second and third screen mean when the page wraps other pages (root, a parent, a feature, the sign-in pages), and adding, cloning, reordering and removing screens in Build Studio, with each screen's own head, styles and code.

A page's content can be split into screens. Each screen is a whole HTML document, stored in the page record's data.screens[]. Visitors see one of them at the page's address; the others can be opened with ?screen=. When the page wraps other pages (the root page, a parent page, a feature's template, the sign-in frame), its screens mean something else: the first is drawn above the content and the second below it.

This page covers screens in depth. Building a page is Build a website page; root and parent pages are Shared header and footer; feature and sign-in frames are Built-in pages and templates; screen variants are Show different content to different visitors.

"Tested" means checked on a local site in headless Chrome, reading which pieces rendered and in what order. "Code" means read from the code, with the file named.

What a screen is

data.screens is an array. A screen's position in it is what the site uses; nothing else orders screens.

FieldHoldsWhat the site does with it
ida unique id; Build Studio makes s-… idsdata.activeScreenId names it; ?screen=<id> shows it
namethe label on the screen chip in Build Studio?screen=<name> (any letter case) shows it; a screen variant names its source screen by name
htmla full document (<!DOCTYPE html>…) or a body fragmentthe head goes into the page, the body is drawn
configvariants, activeVariants and animations, keyed by data-node-idvariants of every screen shown are resolved; animations only for the active screen (below)
slugStudio's "Screen web address name"nothing. No address uses it (base-app build-studio/html-format.ts 147–175 reads only index, id and name)
orderStudio's "Order (for template interleaving)"nothing. Tested: a parent page whose first screen had order: 1 and second order: 0 still drew the first above the page
descriptionfree textnothing
attributeswhat the screen chip's Style button saves (style, class)nothing. Tested: a screen with attributes.style background: blue and a class drew with neither

On the page record next to them:

FieldHolds
data.activeScreenIdthe id of the screen visitors see. Build Studio sets it on every Save (step 3).
data.htmlBuild Studio's copy of the active screen, written on every Save (websitemint build-studio-store.tsx 431–433). The site never draws it while screens exist.
data.bodyClass, data.bodyStyleset on the real <body> if present; otherwise the active screen's own <body> class and style are used (html-format.ts 99–117)

Which screen a visitor sees

The page's address shows one screen. ?screen= picks another (html-format.ts pickScreensToRender 147–175, html-renderer.tsx 183–209):

AddressShowsTested
/<page>the screen whose id is activeScreenId; if no screen has that id (or it is empty), screens[0]yes, both
/<page>?screen=2the screen at position 2, counting from 0yes
/<page>?screen=s-donethe screen with that idyes
/<page>?screen=step%20twothe screen with that name, any letter caseyes
/<page>?screen=allevery screen, one after another, in array orderyes
?screen=99, ?screen=nope, an empty valuethe active screen, as if there were no parameteryes
?screen=Intro&screen=Doneonly the first value countsyes
  • A number is a position first. Tested: on a page whose first screen had the id "2", ?screen=2 showed the third screen. Don't give screens ids that are plain digits.
  • ?screen= only touches this page's screens. The root header and footer and a parent's band wrap whatever it shows (tested with ?screen=all: root header → parent top → both screens → parent bottom → root footer).
  • Every screen is public. Anyone who adds ?screen=<n> sees it. There is no hidden or draft flag on a screen.
  • Search engines see one page. Every ?screen= view has the same title and the same canonical link, the page's plain address (tested; app/[[...slug]]/page.tsx 334–341).

What follows the active screen even when ?screen= shows another:

ThingComes fromTested
The <body> class and styledata.bodyClass / bodyStyle, else the active screen's <body> tagyes: ?screen=0 and ?screen=2 kept the active screen's body class
Animationsthe active screen's config.animations, and only while the page's data.animateContent isn't false (app/[[...slug]]/page-client.tsx 66–89)code

What follows the screen being shown:

ThingComes fromTested
The screen's <head> (<style>, <link>, <script>)each screen shown; a screen not shown loads nothing (html-renderer.tsx 124–145, 202)yes
Variantseach shown screen's own config.variants (html-renderer.tsx 191–197)code (variants themselves are tested in targeted-content-variants)

With ?screen=all, every screen's head applies to every screen. Tested: a <style> in screen 1's head (.mark { color: red }) did not touch screen 2 on its own address, but turned screen 2's .mark text red with ?screen=all. Scope each screen's CSS under an id or class on that screen's own wrapper element.

Screens beat data.html

A page with any screens is drawn from its screens. data.html is not read for the body (html-format.ts isHTMLFormat 17–22; html-renderer.tsx 183–209). Tested: a page whose data.html held other text never showed it, on any address.

  • An empty active screen draws a blank page, not a 404. Tested: the active screen held nothing, a second screen and data.html held text; the address returned 200 with the root header and footer and nothing between. A page counts as "something to draw" when any screen or data.html has content, or when the site has a root or the page a parent (app/[[...slug]]/feature-template.ts 10–18, 47–51), but only the active screen is drawn.
  • Edit the screen, not data.html. Build Studio rewrites data.html from the active screen on every Save. page_write with html writes the active screen and leaves data.html as it was (appengine src/ai/skills/appmint-pages.skill.ts 67–86), so update the mirror yourself if you write through the API.
  • Don't empty screens to make data.html win. The screens are the page.

Screens on a page that wraps other pages

When a page is used around other content, its screens are read by position. activeScreenId and order play no part, and ?screen= doesn't change them.

Used asScreen 1Screen 2One screen onlyThree or moreOnly data.htmlHeads loaded
root or a page's parent, around an ordinary pageabove the pagebelow the pageall of it above, a <footer> in it includedscreens 1 and 2; screen 3 onward never showsall of it aboveyes
A feature's template (a page at /blog, a page chosen in Site Features, account-layout)above the featurebelow itall of it abovescreens 1 and 2all of it aboveyes
The frame of sign-in, My Account, checkout and order pagesabovebelowsplit at the first top-level <footer>: before it above, from it on belowscreens 1 and 2split at <footer>no: only the bodies, plus the frame page's Page settings → Style

Code: html-renderer.tsx extractWrapperFragments 50–75 and 147–163 for the first two rows; components/layout/RootPage.tsx 38–61 for the third. Tested here, on parent pages: one screen (its <footer> stayed above the page), three screens (the third never showed; screen 1's <style> applied), and order reversed (array position won). The feature and sign-in rows are tested in Built-in pages and templates.

Two details from the code for the sign-in frame:

  • A second screen that is empty counts as no second screen, so screen 1 is split at its <footer> (RootPage.tsx 51–53).
  • Which page frames them follows lib/layout-template.ts 4–22: a page with isRoot: true or two or more screens qualifies; a one-screen page qualifies only if it has no <main> or role="main".

A wrapper page opened at its own address is an ordinary page again: its active screen alone (tested: a three-screen parent whose active screen was the third showed only the third at its own address).

On root and parent pages, the first two positions are the header and the footer. Dragging a screen to the front, adding one before them, or deleting one changes what every wrapped page shows. Build extra screens for a wrapper only at position 3 or later, and know they will never show around other pages.

Ask first

  • Which page, on which site? By its address or name.
  • Which screen should visitors see? By name. If the person is changing the live one, say what visitors will see instead.
  • For a new screen: what goes on it, and is it a separate step of the same page or really a separate page? A screen has no address of its own except ?screen=, and search engines don't see it. Something people should find or share is a page.
  • Does this page wrap other pages? If it is root, a parent (some page has parent: <this name>), or a feature's page, positions 1 and 2 are the header and footer of other pages.
  • Is anything on a screen private? Every screen is public through ?screen=.

Steps

1. Build Studio: read the page's screens

Find the page with repo_find on page ({ "data.site": "<site>", "data.name": "<name>" }), then read the whole record with repo_find_one (GET /repository/get/page/<id>; a find result is partial). For each screen note its position, id, name, whether its html has content, and which one data.activeScreenId names.

Then find out whether it wraps anything: it is named root; or repo_find on page with { "data.site": "<site>", "data.parent": "<name>" } returns pages; or the site's data.features names it as a feature's page, or it is named like a feature's address or account-layout.

Hands on: the page id, its screens in order (position, id, name), the active id, and whether it wraps other pages. Check: open /app/build-studio/<page name>. The screen bar above the canvas, to the right of the page name, shows one chip per screen in array order; the canvas opens on the active screen. On the site, /<page>?screen=all shows every screen in that order.

2. Build Studio: add, clone, rename, reorder or remove screens

In Build Studio (websitemint build-studio/screen-navigator.tsx, selective-virtual-dom.ts):

ToIn StudioWhat it does to the record (after Save)
Add a screen+ at the end of the screen bara screen named New Screen with a new s-… id and empty html (no head), added last; the canvas switches to it (selective-virtual-dom.ts 1525–1539)
Copy a screenthe chip's ⋮ → Clonea copy named " copy", with the same html, config (variants, animations), slug and order, added last (1555–1563)
Rename⋮ → Namename. The slug box fills from the name only while it is empty (screen-navigator.tsx 119–126). Links with ?screen=<old name> and screen variants that name the old name stop matching.
Reorderdrag the chip (a grip shows on hover)the array order (reorderScreens). This is what ?screen=<n>, ?screen=all and every wrapper use.
Set Order⋮ → Orderonly the order field. The site ignores it, although the box says "Lower numbers render first when combined with parent page screens" (tested).
Style a screen⋮ → Stylethe screen's attributes. The site ignores it (tested). Style an element inside the screen instead.
Delete⋮ → Delete Screen, then Confirm within 3 seconds; only offered when there are two or more screensthe screen is removed; if it was open, the canvas moves to the first screen (1565–1573)
Hidethere is no hidesee below

Then press Save. Nothing above is stored until then.

The screen open on the canvas when you press Save becomes the live one. Save writes activeScreenId as the screen being edited and copies it into data.html (websitemint build-studio-store.tsx 410–433). Editing screen 2 and saving puts screen 2 at the page's address for everyone. Before saving, click the chip of the screen visitors should see (step 3). On root and parent pages this doesn't matter: they use positions, not the active screen.

Hiding a screen. There is no switch. A screen that isn't active doesn't show at the page's address, but ?screen= shows it to anyone. To take a screen off the site, delete it; to keep its content, first copy the page as a template or clone the screen into another page.

Through the API (no canvas), send the whole screens array back, every screen in the order you want, plus activeScreenId and html (the active screen's document):

  • repo_update on page with patch: { "screens": [ … ], "activeScreenId": "<id>", "html": "<active screen's html>" } (the merge replaces screens as a whole);
  • or POST /repository/update-partial/page/<id> with dot-paths, { "data.screens": [ … ], "data.html": "…", "version": <current> } (tested), never { "data": { … } }, which replaces all of data;
  • or POST /repository/update/<id> with the whole record read from GET /repository/get/page/<id> (tested).

page_write's html only ever writes the active screen. To change another screen, use one of the above.

Stop: on a site that serves drafts, a saved change is live at once. Removing, reordering or renaming screens changes what visitors and existing ?screen= links see; on a page that wraps others, it changes every one of those pages. Say which addresses change and how, and wait for yes.

Hands on: the new list of screens in order (id, name) and the active id. Check: repo_find_one on the page: data.screens in the order you wanted, activeScreenId the screen visitors should see, and data.html the same document as that screen.

3. Build Studio: choose which screen visitors see

In Build Studio, click that screen's chip so the canvas shows it, then press Save. Through the API, set data.activeScreenId to its id and data.html to its html, as in step 2.

Stop: this changes what everyone sees at the page's address. Say which screen will show, and wait for yes.

Hands on: the active screen's id. Check: open /<page> in a browser. The chosen screen is there and no other.

4. Build Studio: each screen's styles and code

WantPut itWhy
CSS for one screena <style> or <link rel="stylesheet"> in that screen's <head>, with selectors under an id on the screen's own wrapper (#step-two .card { … })the head loads only while the screen is shown; with ?screen=all every head applies to every screen (tested)
CSS for every screen of the pagePage settings → Style → CSS (the record's top-level style.css), written with page_write cssloaded whichever screen shows
JavaScriptPage settings → Style → JavaScript (style.javascript), one script for the whole page. To act on one screen, check for that screen's own element first.Tested: the page's script ran on every screen view and found the screen shown by its marker element.
A background or class for the whole screenon a wrapper element inside the screen's bodythe <body> class and style come from the active screen only (tested), and the chip's Style button is ignored
AnimationsBuild Studio's animation panel, with that screen openstored in that screen's config.animations; only the active screen's play on the site (code)

A <script> written inside a screen ran on a full page load of that screen (tested, in its head and in its body), but it doesn't run after the site moves the visitor without a reload, and Build Studio and these docs treat it as unsupported. Use Page settings JavaScript: The AppMint JS engine on a page.

Hands on: nothing new. Check: open each screen with ?screen=<position>: its own styles apply and the other screens' don't.

5. Check every screen on the site

In a browser (not a grep of the page source, which carries the HTML of the screens in a script payload):

  • /<page>: the active screen only, between the site header and footer;
  • /<page>?screen=<n> for each position, and /<page>?screen=all;
  • if the page wraps others, one page it wraps: screen 1 above, screen 2 below, nothing of screen 3;
  • if it frames the sign-in pages, /login.

When it's done

Tell the person: the page's screens in order, by name; which one visitors see at its address; the ?screen= address of each of the others, and that anyone can open those; and, if the page wraps other pages, which screen is drawn above them and which below. Open the page in Build Studio and on the site.

What it can't do yet

  • No hidden or draft screens. Every screen is public through ?screen=.
  • No address per screen. A screen's slug isn't used; ?screen= is the only way in, and search engines index only the page.
  • order and the screen's Style button do nothing on the site, although Studio offers both.
  • Save makes the screen you are editing the live one.
  • The body class and style, and the animations, come from the active screen only, also when ?screen= shows another.
  • Wrappers use only the first two screens, by position. One screen is drawn whole above the content, except around the sign-in pages, where it is split at its <footer>.
  • Screens sent through Build Studio's AI are dropped at Save (code): see Build Studio's AI and the page-building AI tools.

Don'ts

  • Don't edit data.html on a page that has screens; write the screen and keep html as its mirror.
  • Don't empty screens to make data.html show.
  • Don't save in Build Studio with the wrong screen open.
  • Don't give screens plain-number ids; ?screen=2 means position 2 first.
  • Don't put anything private on any screen.
  • Don't add, move or delete the first two screens of root, a parent page or a feature's page without a yes; they are other pages' header and footer.
  • Don't rely on order, slug or the screen's Style button to do anything on the site.
  • Don't style one screen with unscoped selectors; ?screen=all applies every screen's CSS to all of them.