Browse Learn
Static Sites
Build a complete static site from one configuration: your pages and shared partials, your own CSS and JavaScript, a theme, static files and a live preview.
Get the tool
In a Layout UI checkout, run ./layout from the repository root. Elsewhere, download a release archive for your platform and keep its library directory beside the layout binary. Release downloads require access to the Layout UI repository. layout version shows which library a command will use.
layout versionOrganize your site
Keep deployable sources in one root directory. Each page includes the <!-- layout:assets --> marker in its head, where the build inserts component styles and scripts.
my-site/
layout.json
public/
index.html
about/index.html
404.html
_partials/header.html
css/site.css
js/site.js
fonts/brand.woff2
images/<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>About · My Site</title>
<!-- layout:assets -->
<link rel="stylesheet" href="/css/site.7f8593d739202a04.css">
</head>
<body>
<!-- layout:include header.html current="about" -->
<main class="container stack">
<h1>About</h1>
<a class="btn" href="/contact">Get in Touch</a>
</main>
<script src="/js/site.js" defer></script>
</body>
</html>Configure the build
Create layout.json beside the root. Pages resolve and emit relative to root, so public/about/index.html becomes dist/about/index.html.
{
"schemaVersion": 1,
"root": "public",
"pages": ["**/*.html"],
"copy": true,
"exclude": ["drafts/**"],
"siteAssets": { "inline": ["css/site.css"], "hash": true },
"splitting": "shared",
"outDir": "dist",
"report": ".layout/build.json"
}pagesaccepts paths and patterns.*stays within one folder,**spans folders and!patternremoves matches.copycopies images, fonts and other files from the root.excludeskips files from pages and copies.reportkeeps the build report out of the deployable output.- Discovery adds only the components your HTML uses. Plain buttons get Button styles and no JavaScript.
Share markup with partials
Put shared headers, footers and repeated blocks in public/_partials. Parameters fill {{name}} placeholders with escaped text. Partials are never copied to the output.
<header class="container cluster justify-between">
<a href="/">My Site</a>
<nav aria-label="Main" data-current="{{current}}">
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>Inline or hash your CSS and JavaScript
siteAssets.inline turns the listed stylesheets and scripts into <style> and <script> elements. siteAssets.hash writes other referenced files with content hashes so hosts can cache them long term. Production builds minify both. Keep deferred scripts at the end of the body when inlining them.
Set a theme
A theme sets one fixed mode, color tokens, corners and self-hosted WOFF2 fonts. The build adds @font-face rules, font preloads and the page’s data-theme. Custom tokens such as code become CSS custom properties.
"theme": {
"mode": "dark",
"colors": {
"bg": "#0a0a0b", "surface": "#111113", "soft": "#141417",
"ink": "#ededef", "muted": "#a1a1aa", "line": "#26262b",
"accent": "#5ee38a", "accent-hover": "#8cf0ab", "tint": "#12301d",
"on-accent": "#04210f", "code": "#b4f0c6"
},
"radius": 10,
"fonts": [
{ "family": "Brand", "src": "fonts/brand.woff2", "weight": "400 700", "preload": true }
],
"fontFamily": "\"Brand\", ui-sans-serif, system-ui, sans-serif",
"contrast": [["code", "bg"]]
}Text colors must reach 4.5:1 against their backgrounds. Add your own pairs to contrast. A failing pair stops the build before any output changes:
layout: theme contrast check failed for 1 pair:
muted #4a4a50 on bg #0a0a0b: 2.37:1 (needs 4.5:1)Build and preview
Run these from the folder that contains layout.json. layout dev builds, serves the output at localhost:8080 and rebuilds when your sources, configuration or the library change. Build errors appear in the page until you fix them. Deploy the dist directory from layout build.
layout dev --port 8080
layout build --jsonEach build removes outputs it no longer produces and waits for any other build of the same directory. Discovery reads configured HTML, not JavaScript-created markup or framework templates; declare runtime-only components in the configuration.
Deploy from CI
Download a pinned release in your workflow instead of committing build output. Release downloads need a token with read access to the Layout UI repository, stored as a secret such as LAYOUT_UI_TOKEN. Pin the archive checksum from the release’s SHA256SUMS.
env:
LAYOUT_VERSION: 0.1.0
LAYOUT_TARGET: x86_64-unknown-linux-musl
LAYOUT_SHA256: <checksum from the release SHA256SUMS>
steps:
- uses: actions/checkout@v4
- name: Install Layout UI
env:
GH_TOKEN: ${{ secrets.LAYOUT_UI_TOKEN }}
run: |
name="layout-$LAYOUT_VERSION-$LAYOUT_TARGET"
gh release download "layout-v$LAYOUT_VERSION" --repo vividmachine/layoutui --pattern "$name.tar.gz" --dir "$RUNNER_TEMP"
echo "$LAYOUT_SHA256 $RUNNER_TEMP/$name.tar.gz" | sha256sum --check
tar -xzf "$RUNNER_TEMP/$name.tar.gz" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/$name" >> "$GITHUB_PATH"
- run: layout build --json