How a Common site fits together

Every page on this site starts with the same three lines. This page explains what they do, how the files share their work, and the handful of patterns that keep a site of twenty pages as easy to change as a site of one.

<cl>
  set title to "Blog"
  include "header.html"
</cl>

That is the whole trick. The rest of this page is what happens when it runs.

1. include: one file, used by many pages

include "file.html" puts another file into the page at that spot, and runs its <cl> blocks as if you had typed them there. Two things make it powerful:

# parts/kit.html
<cl>
  set site to "My site"
  to badge label
    show "<span class='badge'>[label]</span>"
  end
</cl>

# any page
<cl>
  include "parts/kit.html"
  show "<h1>[site]</h1>"
  badge "new"
</cl>

The file name is always written by you, relative to the page's own folder: "header.html" or "parts/menu.html". Common refuses .., a leading /, and any name that came from a visitor, so nobody can make your page include a file you did not choose.

2. The include chain on this site

Open any page here and this is what runs, top to bottom. Each box is a file; each arrow is an include.

  1. blog.htmlsets title, then includes the header
  2. header.htmlthe <head>, the theme, the navigation. Needs title, so the page must set it first
  3. settings.htmlnames the whole site shares: its name, tagline, this year
  4. database.htmlwhere the data lives: one switch for SQLite or Postgres

Then the page does its own work, and finishes with include "footer.html". Because the names flow down the chain, every page can use the settings without including them itself. Right now they say:

full_nameCommon (CL)
taglineCode that reads like English
this_year2026
database_kindSQLite

Change the site's name in settings.html and it changes in the title bar, the navigation and the footer of every page at once.

3. The folder

A Common site is a folder of files. This one, grouped by what each file is for:

Pages: one per address
index.html is /, blog.html is /blog and /blog/any-post, learn.html, quest.html, contact.html, …
Shared parts: included, never visited
header.html, footer.html, settings.html, database.html
Data files: lists kept in one place
lessons.html (16 lessons, used by Learn and Practice), quests.html (10 levels, used by the Quest)
Action pages: do something, then send the visitor on
logout.html, theme.html
Special
404.html, shown for any address with no page
Plain files: sent as they are
theme.css, and any images or fonts
Private: Common never sends these
site.db, site.sql, cookies.db, cl.secret, and the outbox/ folder and mail.log that send email makes. Any file ending in .db, .sqlite, .sql, .secret, .eml or .log is refused, whatever its case.

4. Seven patterns worth copying

The settings file

Anything you might want to change later, and anything more than one page uses, gets a name in one file. Pages read the name, never the value.

<cl>
  # settings.html: included by header.html, so every page has these
  set full_name to "Common (CL)"
  set tagline to "Code that reads like English"
  set this_year to year of today
  include "database.html"
</cl>

The layout: header and footer

Each page sets what makes it different (its title, maybe a description), includes the header, writes its own content, and includes the footer. The order matters: the header shows title, so set title must come before include "header.html". Swap them and Common stops with I do not know what 'title' is.

A shared file that needs names set before it should say so in its first lines, so nobody has to read the whole file to find out. Keep that list short: one or two names. This site's header starts:

<cl>
  # Needs: title (set it before including this file), like
  #   set title to "Blog"
  #   include "header.html"
  include "settings.html"
</cl>
<!DOCTYPE html>
<html lang="en">
<cl>
  set title to "Contact"
  include "header.html"
</cl>

<section class="container">
  <h1>Contact us</h1>
</section>

<cl>
  include "footer.html"
</cl>

The data file

When two pages show the same things, keep the list in a file of its own. The Learn page shows the lessons and the Practice page opens them in an editor; both include lessons.html, so a new lesson appears in both.

<cl>
  # lessons.html: lists only, no HTML
  set lesson_titles to "Say hello", "Remember a value", "Math"
  set lesson_codes to "show \"Hello\"", "set x to 1", "show 2 + 2"
</cl>

The action page

Some addresses are not pages to look at but things to do: sign out, switch the theme, save a form. They do their work and send the visitor somewhere with go to, so a refresh never does it twice. They need no HTML at all.

<cl>
  # logout.html: the whole file
  forget everything
  go to "/"
</cl>

One page, many addresses

/blog/hello-world has no file of its own: the server finds blog.html and the page reads the rest of the address from path. One file then serves the list and every post.

set slug to last of split path by "/"
if slug is "blog"
  set posts to ask database "select * from posts order by posted desc"
otherwise
  set posts to ask database "select * from posts where slug = ?" with slug
end

One switch for the database

Pages never write a database file name. They write open database site_database, and database.html says what that is. Moving from SQLite to Postgres is a one-line change in one file.

<cl>
  # database.html
  set site_database to "site.db"
  # set site_database to "postgres://user:password@host/dbname"
</cl>

A verb for repeated markup

When the same piece of HTML appears for every item, name it. The blog's list draws each post with its own verb, so the loop stays one line.

to show_post post
  show
    <article>
      <h2><a href="/blog/[slug of post]">[title of post]</a></h2>
      <p>[date of posted of post]</p>
    </article>
  end
end

for each post in posts
  show_post post
end

5. A starter site you can copy

Four files are a complete site with a shared layout. Make a folder, add these, and run cl serve 8000 my-site.

settings.html
<cl>
  set site_name to "My site"
</cl>
header.html
<cl>
  include "settings.html"
</cl>
<head>
  <meta charset="utf-8">
  <title><cl>show "[title] - [site_name]"</cl></title>
</head>
<body>
<nav><a href="/">Home</a> <a href="/about">About</a></nav>
index.html
<!DOCTYPE html>
<html>
<cl>
  set title to "Home"
  include "header.html"
  show "<h1>Welcome to [site_name]</h1>"
  include "footer.html"
</cl>
footer.html
<footer>
  <cl>show "[site_name], [year of today]"</cl>
</footer>
</body>
</html>

Add about.html the same way as index.html and the About link works at /about.

6. When it does not work

Common saysWhat happened
I do not know what 'title' isThe header was included before the page set title. Move set title above the include.
cannot find the file "…"The name is relative to the page's folder. A page in shop/ includes "header.html" from shop/, not from the top.
not a path with .. or a leading /Includes stay inside the site folder. Keep shared parts beside the pages, or in a folder under them.
cannot take a name that came from a visitorThe file name was built from query, form or path. Choose between fixed names with if instead.
files include each other in a loopa.html includes b.html, which includes a.html. Common stops after ten levels.
Raw <cl> text in the browserThe page was opened as a file, not through Common. Use cl serve, or set up CGI as in the deploy guide.

Next: build a guestbook using these pieces, or look any word up in the glossary.