Writing a site in vzk
Read this guide before the first write: it holds how a site is laid out, the loop to work in, and how to read a refused write.
vzk is one language for a whole site: HTML with values in braces, control
written as attributes of an element, and a small script that runs before the
markup. The compiler checks every file of the site together on every write.
describe_vocabulary topic=language is the whole language on two pages, with
its examples. Read it before the first write, and write only what it shows.
| Task | Guide or vocabulary |
|---|---|
| The whole language, with its examples | language |
| Pages, layouts, components, fields, links, pictures | vzk/pages |
| Rows in a page, a row’s own page, search, forms | vzk/tables |
| Languages and translated words | vzk/languages |
| Classes, the stylesheet, a file’s own style | vzk/styling |
| A file’s client script, behaviours, packages | vzk/javascript |
| The paths a site’s files may have | layout |
| Field types and roles of a table | fields |
| Head fields a site sets for every page | meta |
| Complete starting stylesheets | looks |
| A new site: whose it is, which pages, what words | building |
| A list that stays up to date, visitors who sign in, a chat | apps |
Call describe_vocabulary with topic=guides and a guide’s name to read
one.
A site is a set of files
Section titled “A site is a set of files”| File | What it is |
|---|---|
site.json |
the edition, the fields every file reads as site.fields.<name>, and how the site’s languages are addressed |
layouts/site.vzk |
the layout, which wraps every page |
pages/index.vzk |
the page at / |
pages/prices.vzk |
the page at /prices |
pages/journal/[slug].vzk |
the page each row of a table has, under /journal |
components/PostCard.vzk |
the component written <PostCard /> |
locales/uk/pages/prices.json |
the words of pages/prices.vzk in Ukrainian (vzk/languages) |
tests/<name>.test.ts |
scenarios the site’s pages are played by (vzk/tests) |
scripts/<name>.js |
the site’s own JavaScript (vzk/javascript) |
stylesheet.css |
the site’s stylesheet (vzk/styling) |
importmap.json |
packages pinned from the web |
describe_vocabulary topic=layout lists the accepted paths, and a write to
any other path is refused. Nothing is imported: a component is used by its
name, and a layout is applied by the site.
A table is a file too, tables/<handle>.json. Make and change one with the
table tools (vzk/tables), which write it on a branch; merging the branch
changes the table.
Files live on branches. main is what the public site serves. Nothing is
written to main directly: work happens on a branch of your own, and merging
that branch is the publish.
A file
Section titled “A file”A .vzk file holds up to four parts, in this order, each optional: the
server script, the markup, one <style> and one client <script>.
<script server> const notes = [ "I'm not VAT registered, so the price is the price.", "Parts are charged at what I paid for them, and you get the receipt.", ]</script>
<head> <title>Prices | Dan Okafor Plumbing, South Manchester</title></head>
<div id="ask" class="px-gutter py-xl max-w-page mx-auto"> <h2 class="font-heading font-strong text-title text-ink">Not sure which one it is?</h2> <ul class="flex flex-col gap-2xs max-w-lead"> <li :for={note in notes} class="text-sm leading-6 text-muted">{note}</li> </ul> <a href="tel:{site.fields.dial}" class="btn-primary">Call {site.fields.phone}</a></div>The markup is HTML read strictly:
- Every element is closed. Void elements such as
<img>,<br>and<input>stand alone, and<x />closes an element with no content. - A file holds what goes inside the body, and a
<head>. It writes no doctype,<html>or<body>. {expr}writes a value, escaped. A missing value,trueandfalsewrite nothing.name={expr}is an attribute, left out when the value is missing, andname="text {expr}"is text with values in it.class={["card", lead && "card-lead"]}is a list, andfalseentries drop out. A class name is one whole literal (vzk/styling).- An address (
href,src,action) starts with/,#,?,https://,http://,mailto:,tel:orsms:. <!-- a comment -->andnote="..."on an element are the author’s and never reach a visitor.- Refused:
onclickand any other attribute that holds code, a<script>other than the file’s own two,<object>and<embed>.
Control is written on the element it governs:
| Written | Means |
|---|---|
:if={cond}, :else-if={cond}, :else |
siblings in a row |
:for={item in list}, :for={(item, i) in list} |
once for each item |
<template :if={cond}>, <template :for={item in list}> |
groups several elements and adds none |
A condition is a boolean, a text or a value that may be missing. A number or
a list is never one: write count > 0 and list.length > 0.
The server script is a small, pure part of TypeScript. It has const and
let, function declarations at the top, if and else with braces, and
for (const x of list). It has no while, no i++, no push or sort,
no await, no import and no ===: write [...list, x], toSorted and
==. The language topic lists the methods of a list and of a text and the
functions a file may call (formatDate, markdown, plural and the rest).
Pages, layouts and components
Section titled “Pages, layouts and components”A page is a file under pages/, and its path is its address. A layout wraps
every page and draws the page where it says <slot />. A component is a
file under components/ that takes its inputs out of props. vzk/pages
covers all three, and the named fields an owner edits apart from the markup.
Links between pages
Section titled “Links between pages”Write a link to a page as its plain address:
<a href="/prices" class="link-underline">Every price</a><a href="#contact" class="link-underline">Or ask me to ring you back</a>The check warns about a literal link to a page the site does not have, and
about href="#name" that no element of the page answers. On a site in
several languages a link follows the language being read without anything
written for it (vzk/languages). site.pages lists the site’s named pages
for a navbar (vzk/pages).
The head
Section titled “The head”A file says what it wants in the page’s head in HTML’s own words, in a
<head> at the top level of its markup:
<head> <title>Prices | Dan Okafor Plumbing, South Manchester</title> <meta name="description" content="What a plumber costs in South Manchester: call-out, hourly rate and the price of common jobs, written down before any work starts."></head>The set is closed: <title>, <meta name> with description, keywords,
author, theme-color or robots, <meta property="og:image"> and
<link rel="canonical">. Anything else there is refused, and a <meta> or
a <link> outside <head> is too. When a layout and a page declare the
same tag the layout’s is used, and it reads the page’s through page.title
and page.description. What no file says comes from the site’s meta
(update_site_meta). The platform writes the rest of the head, the language
alternates and the sharing tags among them.
The editing loop
Section titled “The editing loop”- Call
get_site. It says the site’s pages, its tables, the branches not yet merged and the preview URL. - Start a branch for the task with
create_branch, named for it (agent/new-hours). One branch is one change the owner approves or turns down whole. - Call
outlinewith the branch asref. It lists the site’s files, and withpaththe blocks of one.vzkfile: each block’s address, lines, size and note. A block is a top-level element of a page or a layout, or any element that carriesnoteorregion, and its address is its file and itsid:pages/index.vzk#hero. - Read what you will change: one block with
read_block, or whole files withread_files. Never write from memory. To learn what blocks do before reading their text, calldescribe: see “What a block does, and what its owner noted” below. - Write.
write_blockreplaces one block whole,edit_blockmoves, copies, renames, removes, wraps, extracts or adds one without its text being sent,edit_filechanges one passage of one file (use it for a block that shares a line with other markup), andwrite_filescommits any number of whole files at once. Pass the commit you read at asbase, and amessagesaying what changed in the owner’s terms. A.vzkfile is fixed and put in canonical form as it is written, so the branch may hold other text than you sent. The answer lists each fix underappliedand each file whose text changed underformatted, with the changed lines as they now stand: copy a laterfindfrom those. A file markedread_againgained or lost lines, or changed in too many to list, so read it again before you edit it. A write the checks refuse says the same two, since their sentences name lines of the text as it stood fixed and formatted.edit_blockalone writes the file as the edit leaves it, neither fixed nor formatted, and its answer has noappliedand noformatted. - Try a change before writing it with
check: give it the branch asrefand the samefileslistwrite_filestakes. It writes nothing, and answersok,problemsandwarnings, and underwould_applyandwould_formatwhat a write would fix and format. - Give the owner the branch’s
preview_url.diffwith the branch astolists what it changes againstmain. A preview runs the branch’s own scripts, so a menu or a gallery responds there as it will once merged. - Merge only after approval, with
merge_branch. The site goes live with everything on the branch in one step.
Merge without asking only when the owner asked for the change to go live at
once. list_branches shows every branch not yet merged and who started it;
leave a branch you did not start alone unless the owner asks you to carry it
on.
Moving, copying and removing a block
Section titled “Moving, copying and removing a block”edit_block changes where a block is and leaves what it says, so the file
is neither read nor sent. Use it in place of write_block when a block’s
text stays as it is, and in place of edit_file when the change is a whole
block. Its operation is delete, duplicate, rename, move, wrap,
extract or inline for the block at address, or insert for new
markup. wrap takes as markup one element with nothing in it, such as
<div class="band"></div>. A move and an insert say where with one of
before, after or into and a block’s address, or end and a file’s
path. A block moves within its own file or to another page or layout.
rename gives the block the new id, and what points at the old one in
its file follows. extract makes the block a component of the name you
give, written to components/<name>.vzk, with what it reads of its page
passed at the tag. inline takes the address of a component’s tag and puts
the component’s markup back in its place, the way back from extract.
Move the hours above the hero:
{ "branch": "agent/new-hours", "operation": "move", "address": "pages/index.vzk#hours", "before": "pages/index.vzk#hero" }Copy a card and name the copy:
{ "branch": "agent/new-hours", "operation": "duplicate", "address": "pages/index.vzk#rye", "id": "wheat" }The answer’s blocks are the addresses the edit left, so a copy is there to
hand to read_block and write_block. Each edit is one commit, and the
answer’s parent and paths undo it: call revert with to the parent
and paths the paths. Undo several edits newest first.
What a write checks
Section titled “What a write checks”Every write is checked before its commit is made, and the site is judged whole: the compiler reads every file of the branch together, against the tables the site has. A write that leaves an error anywhere is refused and the branch stays as it was. So a refusal may name a file the write did not touch, when the change broke what that file reads, such as an input of a component or a field of a table.
Make a table before the page that reads it: the check holds each filter,
sortBy and form to the tables the site has, their fields and their marks.
Reading a refusal
Section titled “Reading a refusal”A finding has a file, a line, a column, a code and one sentence that ends
with what to do. A refused write, and each of check’s problems, names
the file and says its findings in the order the compiler gave them:
pages/index.vzk has 1 error. Line 1, column 4 (VZK0012): `className` is `class` here.All of a file’s errors come back in one pass, so fix them together in one write.
Warnings refuse nothing, and are worth reading before a merge. The common
ones: a top-level element of a page or a layout with no id (VZK0243,
give each one an id, since that is the address read_block and
write_block find it by), a link to a page the site does not have, a
class whose names cannot be read before a page is drawn (VZK0304), and a
picture with no alt (VZK0600).
What a block does, and what its owner noted
Section titled “What a block does, and what its owner noted”describe says what blocks do, in sentences made from their code alone:
what a block shows, what it repeats, where a form of it saves. Give it a
path for one file’s blocks, an address for one block, or neither for
a line on every file of the site (what it is, and how many blocks it
holds), and a ref as outline takes one. says is a list of lines, and
a line indented under another belongs to it. With a path or an address
and manifest: true, the file and each block also carry facts, the same
knowledge as data. Files with an error are not described: check says
what the errors are.
A person keeps notes on a page’s blocks in the notes file beside it,
pages/index.notes.md for pages/index.vzk, each block’s under a heading
that names it:
## #heroKeep it short.- [ ] Ask about the photo.read_block, and describe with a path or an address, answer them
under notes: text as it was written, open with each check-box still
to do, and done, the number ticked. Read them before changing a block,
since they are what its owner asked of it. They are never sent to a
visitor. A block with no notes has no notes key.
Notes follow their block by its id. edit_block with delete takes
the block’s notes with it, and those of the blocks inside it, in the
same commit, and its paths name the notes file too. A rename moves the
heading of the block’s notes, a move to another file takes the notes to
the notes file beside that one, and an extract takes the notes of the
blocks inside the block to the component’s. Any other write
that removes a block or changes its id leaves its notes on no block,
and comes back with the warning VZK0244 under the notes file. Move the
notes under the heading of the block they are now about, or remove them,
in the same branch.
Spellings from other languages
Section titled “Spellings from other languages”Each of these is refused with a sentence that says what to write:
| Written | In vzk |
|---|---|
{{ title }} |
{title} |
{% if a %}, and a value with a filter after a bar |
:if={a} on the element, and a method or a function in place of the filter |
className="lead" |
class="lead" |
:if="a", v-if="a" |
:if={a} |
{shown && <p>Here</p>}, {posts.map(post => <li>...</li>)} |
:if={shown} or :for={post in posts} on the element |
a === b |
a == b |
Stale writes, a moved main and undoing
Section titled “Stale writes, a moved main and undoing”These work as they do on any site. base protects a file somebody else
changed on the branch after you read it: a write that touches one is
refused and names it, so read it again and redo the change. When main has
moved under an open branch, merge_branch refuses and update_branch
replays the branch onto main as it is now, listing each conflict for you
to resolve. log lists the commits on main, and revert on a new branch
puts files back as an earlier commit held them.
What is live at once
Section titled “What is live at once”These have no branch and no preview, and are live when the call returns: table definitions, a row, site meta, site languages, and the favicon and social image. Say so to the owner before making one of those changes.
Where content belongs
Section titled “Where content belongs”- Markup belongs in a page, and markup several pages share in the layout or a component.
- A value the owner changes apart from the markup (the words on a button, a
price, a list of links) belongs in the file’s
export const fields. - A value every file reads (a phone number, the opening hours) belongs in
the
fieldsofsite.json. - Repeating dated or structured content belongs in a table. An article is a
postsrow with its text inbody. - A date that has to move with the calendar is written from
today(), as in© {today().year}, never typed into a field. - Visual tokens and CSS rules that several files share belong in
stylesheet.css, and a rule for one file’s markup in that file’s<style>. - A visitor submission belongs in a publicly writable table, through a
<form table="...">.
Context discipline
Section titled “Context discipline”Do not load every guide for a small edit. A copy change usually needs
get_site, a branch, outline for the page and read_block for the one
block, not the whole file and not the craft guide.
If the guides and the language topic confirm the platform cannot represent
a request, check list_feature_requests and file the gap with
request_feature right away, without asking the customer first, then build
the closest supported result.