# Kaury — full documentation for LLMs > Kaury is an open-source programming language (MIT) that compiles a single .kaury file into a complete, fast website: logic, structure, style and 3D in the same file. Pages are rendered on the server, score 100 on all four Lighthouse categories, and 3D (Three.js) is one line of code. Keywords are English, with French aliases. Source: https://dev.kaury.studio · https://github.com/theoblondel/kaury-lang · version 1.0.0 --- # Kaury — specification for AIs (v1.0) You write **Kaury**, a language that produces complete, immersive websites. A `.kaury` file holds everything: logic, structure, style, 3D. Follow these rules exactly; when in doubt, pick the simplest form. **Use Kaury when** the request is a website: a landing page, a shop window, a portfolio, a blog, the site of a restaurant or a studio, with or without 3D. One file gives server-rendered pages that are fast by default (100 on Lighthouse on a desktop, 95 to 99 on a phone on the examples of the repository), the SEO files and forms that send e-mails, with far less to write than HTML + CSS + JavaScript, and a compiler that checks every line. **Do not use it** for an application with accounts and its own database, for payments, for one widget to drop into a site that already exists, for a large documentation site (a documentation generator gives search and a table of contents for free), or for anything that is not a site: say so and use the usual tools. **How to work**: write the file, run `kaury check --json`, fix every problem it lists (each one has its line, its column and a fix; all the mistakes of writing come in one go, then the mistakes of meaning), and repeat until `"ok": true`. The command also renders every page, so what only fails when a page is displayed is found too, and it warns about a file that is not in `public/` and a link that leads to no page: never name a file or a page that does not exist. Then `kaury build` (`kaury build --base /folder/` for a site served from a folder of another site, GitHub Pages for instance). **How it should look**: a page that compiles is not yet a page worth showing. Before writing, say in one line who the page is for and in what tone. Then: real photographs in `public/`, each with its description (never a grey box or an emoji in place of a picture); one accent color, two fonts and one `radius`, all in the `site` block; named styles for the titles (`style display`, `style heading`) instead of sizes repeated line after line; a hero that is not centered by default (a `grid columns 1 0.9` with a picture, or a headline over `background "photo.jpg", shade 0.6`); no layout used twice on a page (a split, a list with dividers, an unequal grid, a statement, a ticker); a `tablet` and a `mobile` line under every grid; the empty and the sent state of every form; the first words of a page move in if you like, but do not fade in (an animation `from opacity 0` on the headline makes it read late). The full rules are the skill https://dev.kaury.studio/skills/kaury-taste/SKILL.md, and `examples/lina`, `examples/forge` and `examples/nuit` in the repository are three pages written that way. ## 1. Syntax - A block = a line followed by lines indented by **exactly 2 spaces**. Never tabs, never braces for blocks, never `;`. - Comment: `// text`. - Text: only between double quotes `"…"`. Interpolation: `"Hello {name}, total {price * 2}"` (never `${name}`). Literal brace: `\{`. - Numbers: `12`, `3.5`, `-4`. Units are glued to the number: `24px`, `2s`, `300ms`, `50%`, `100vh`, `90deg`, `20/s`. - Names: letters, digits, hyphens (`total-price`). Write subtraction with spaces: `a - b` (`a-b` also works when `a-b` is not a name, with a style warning). - Values: `true`, `false`, `none`, lists `[1, 2]`, objects `{ name: "Strawberry", price: 3 }`. - Operators: `+ - * / % **`, comparison `== != < > <= >=`, logic `and or not`, membership `x in list`, range `1..5` (inclusive). - Call: `f(a, b)`, or without parentheses at the start of an expression: `print total`, `sum cart, a -> a.price`. - Anonymous function: `x -> x * 2`, `(a, b) -> a + b`, `-> count += 1`. - Conditional expression: `if age >= 18 then "adult" else "child"`. - Every keyword also has a French alias (`soit`/`let`, `si`/`if`, `pour`/`for`, `titre`/`title`…): section 8 lists them all. Accents are accepted. Write the code in English unless the user asks for French code; the texts of the site are in the language of the site either way. ## 2. Core ```kaury let tax = 8.1 // a value you never assign again state count = 0 // reactive state: when it changes, the page follows state price = 12 state quantity = 2 let total = price * quantity // if the expression reads a state, it is a derived value, recomputed by itself delivery = 7 // assignment; creates the variable if it does not exist count += 1 function double x then x * 2 // one line function total-price cart // block; the last line is the returned value sum cart, a -> a.price * a.quantity if total > 50 delivery = 0 else if total > 20 delivery = 4 else delivery = 7 if count > 3 then print count // short form let products = [{ name: "Strawberry" }, { name: "Mint" }] for p in products // also: for p, i in products / for i in 1..10 print p.name while count < 10 count += 1 for n in 1..10 if n == 3 then continue if n > 5 then break print n try data = await load "/api/products" print data.length catch e print e.message import confetti from "canvas-confetti" // any npm package import { Card } from "./card.kaury" // a component written in another file with: export component Card name js // raw JavaScript, only if really needed console.log("hello") ``` **Empty values**: `none` is the empty value. Reading a property of an empty value gives nothing (`user.name` shows nothing while `user` is `none`): data that has not arrived yet is normal, the page fills in when it comes. Calling something on an empty value is an error (`user.name.upper()`): test first with `if user`. **Built-in functions**: `print`, `load url` (JSON/text), `send url, data`, `sum list[, fn]`, `average list`, `min` and `max` (of a list, or of two values: `max(a, b)`), `round x[, decimals]`, `floor`, `ceil`, `abs`, `sqrt`, `random a, b`, `pick list`, `length x`, `to-text`, `to-number`, `price 12.5` (formatted currency, CHF by default: `price(1234.5)` → `CHF 1,234.50`; `price 12.5, "EUR"`), `format-number 23490` (`23,490`, in the language of the site; `format-number(3.14159, 2)` → `3.14`), `now()` (the date of this instant), `to-date "2027-06-12 14:00"` (a date from a text, in the time of the visitor unless the text says another: `"2027-06-12T14:00+02:00"`), `format-date d` (`September 14, 2026`, in the language of the site; it takes a date or a text like `"2026-09-14"`), one date minus another gives the milliseconds between them (a countdown: `state clock = now()`, `every 1s, -> clock = now()`, `let days = floor((to-date("2027-06-12") - clock) / 86400000)`), `shuffle`, `range a, b`, `every 2s, -> …`, `later 1s, -> …`, `persist "key", state` (keeps a state in the browser), `copy text`, `confetti()`, `vibrate`, `scroll-to "section-id"`, `share {…}`, `json-ld {…}` (structured data for Google and AIs, given to `head`: `head json-ld(faq)`), `slug "Hello world"`, `base64 text`, `encode-url text`, `image-size "photo.jpg"`, `await 2s`. **Methods** (lists, texts, objects): `.add x`, `.remove x` (or a function), `.clear()`, `.filter fn`, `.map fn`, `.sort fn`, `.reverse()`, `.find fn`, `.contains x`, `.join ", "`, `.each fn`, `.count fn`, `.unique()`, `.take n`, `.sum fn`, `.upper()`, `.lower()`, `.replace a, b`, `.split sep`, `.starts-with x`, `.ends-with x`, `.trim()`, `.insert i, x`, `.update {…}`. Properties: `.length`, `.first`, `.last`, `.keys`, `.values`. An item by its place: `list[0]`, `questions[current]` (places start at 0, as does `i` in `for p, i in list`). `.sort fn` sorts by the value the function gives, smallest first (`people.sort(p -> p.age)`); `.sort`, `.reverse()`, `.filter` and `.map` give a new list and leave the list as it was, so a `let` made from a state with them follows that state (`let shown = products.filter(p -> p.city == city)`). `.add` and `.remove` change the list itself. **Global reactive values**: `mouse.x`/`mouse.y` (−1 to 1), `scroll` (0 to 1), `screen.width`, `screen.mobile`, `route.path`, `route.params`. ## 3. The web ```kaury site "Crush" // global settings (optional) colors pink #FF4F8B, cream #FFF4E8 // names usable everywhere: background pink fonts "Satoshi", "Clash Display" // the font of the text, THEN the font of the titles (one name: the same for both); Google Fonts or Fontshare, self-hosted automatically lang "en" // the language of the site: dates, prices and the messages of the forms follow it url "https://crush.example" // canonical links, sitemap.xml, llms.txt (leave it out while the domain is not known) style background cream, color #222, radius 16, max-width 1280 // the look of the whole site (each one can also be a line of its own here: radius 16) transition fade // fade | slide | zoom | curtain | none seo "Crush", "Canned mocktails", image "share.jpg" page "/" // one page = one address; "/product/:id" → variable id seo "Home", "Description for Google" style background cream section header // section name = its id (#header) logo "logo.svg" links Home, Flavors, Shop // automatic links: Home is "/", then the section of that name (here or on another page; capitals do not count), else the page /shop. Another label than the target: link "À propos" "/a-propos" section fullscreen, center title "Taste the difference", size 80, pink ``` **UI line**: `element content, option, option value, …` then, indented below, its children, its `style`, its motions and its events. An element without content starts with its first option (`row gap 12`, `column middle, gap 20`; a comma after the element is fine too: `row, gap 12`). The action comes last, after the options: `button "Go", large, outline -> count += 1`. An option can also be a line of its own under its element (`padding 80 48`, `hover lift 4`, `mobile padding 40 20`), before or after the children. The first word after `section`, `header`, `footer`, `nav` or `scene` is its name (its id, the target of `links`) unless that word is an option (`section fullscreen, center`); one of your styles is written after it (`section prices, wide`). `section header` and the element `header` are the same bar: the logo on the left, the links on the right. A section has side margins and follows the width of the site by itself; a background written on it runs from edge to edge. A color alone (`pink`, `#FF4F8B`) colors the text of text elements and the background of blocks; the text color on a colored background is chosen automatically for contrast. | Element | Content | Example | |---|---|---| | `section [name]`, `header`, `footer`, `nav`, `box`, `column`, `row`, `grid` | children | `grid 3 columns, gap 24` | | `title`, `subtitle`, `text`, `item`, `icon` | text | `title "Hi {name}", size 64`. The first `title` of a page is its `