This repository holds the source code and content for the Commonway System guidebook, published at commonwaysystem.org. It's a working SveltePress site running a fully custom "Commonway" theme: the same layout shell people like about SveltePress's default theme (sticky navbar, collapsible sidebar, on-this-page rail, prev/next footer), rebuilt from scratch with Commonway System branding and a set of CS-specific components (Pattern ID card, Retrofit Strategy card, density tier chip, citation badge, evidence-tier chip, Media Gallery, plus the Brand Guide preview components: color swatch, logo preview, example card, button, form controls preview, type sample). It also has search across every page, a display-settings panel (text size, contrast, simplified view), a plain text-only version and a markdown version of every page, and a human-readable site map.
This file walks through getting a local copy of the publication running on your own computer. A local copy means your computer builds and serves its own private version of commonwaysystem.org, visible only to you, that matches whatever files are in this repository at that moment.
You don't need to be a professional developer to follow these steps. This project welcomes contributions from urban planners, engineers, and anyone with relevant subject-matter expertise, not just coders. A working local copy is how anyone, technical or not, previews their own edits before submitting them.
Why you'd want this: every page in the published guidebook, pattern pages, retrofit strategies, guide chapters, is a plain text file in this repository. If you want to fix something or add content, you edit that file, use your local copy to see exactly how your change will look on the real publication, and then submit your edited file back to the project as a pull request, a formal request asking the maintainers to review your change and merge it into the live publication. Setting up a local copy is the first step in that process for everyone, regardless of technical background.
Git and GitHub are the tools this project uses to track changes and let people propose edits. Two terms worth knowing going in:
- Fork: your own personal copy of this repository on GitHub, which you edit freely without affecting anyone else's copy or the live publication.
- Pull request: once you've made changes in your fork, this is how you formally propose merging them into the real project.
GitHub's own guides on forking a repository and about pull requests cover the actual click-by-click steps better than this file could. Once you have your own fork, the instructions below pick up from there: downloading a copy of your fork onto your computer (this is what "running it locally" means), then starting it up.
- Node.js 24 or later. Node.js is the runtime this project's code runs on (Netlify builds the live publication with Node 24, and the build's helper scripts rely on Node 24's built-in TypeScript support). Download it here if you don't already have it.
- pnpm. This project's package manager (a tool for downloading and
managing the code libraries the project depends on), similar to npm but
faster. The lockfile in this repo (
pnpm-lock.yaml/pnpm-workspace.yaml) requires pnpm specifically, not npm. If you already have Node.js, install it withnpm install -g pnpm; otherwise see pnpm's own install guide.
pnpm install
pnpm run devpnpm install downloads everything this project's code depends on. You
only need to run it once, or again later if those dependencies change.
pnpm run dev starts your local copy; leave it running in its own
terminal window while you work, it automatically updates to reflect any
file you edit and save.
Then open the URL it prints (usually http://localhost:5173).
Other commands you may need:
pnpm run check # type-checks the Svelte and TypeScript code
pnpm run build # the full static build, described below
pnpm run preview # serves the build/ folder locally to sanity-check it
pnpm run thumbs # makes small copies of new or changed images (see "Adding images")You don't need pnpm run build to contribute content: pnpm run dev above
is enough to preview any edit. The build matters mainly to maintainers doing
a real deploy. It writes plain HTML/CSS/JS to build/, no server required
at runtime, then runs three more steps: it writes a plain text-only version
of every page (build/text/) and a markdown version of every page
(build/<page>.md, plus build/llms-full.txt), builds the search index
(build/pagefind/), and checks that every image has its small copies. Any
static host works; this publication currently deploys via Netlify, triggered
automatically on push to GitHub.
Because those extra steps happen after the main build, the text-only pages,
the markdown files, and (until you have run a build once) search do not exist
under pnpm run dev. Links to them will 404 there, which is expected. After
one pnpm run build, the dev server serves that build's search index, so
search works while you edit (results lag your latest edits until the next
build).
src/
routes/ Every page. A folder = a URL segment.
+layout.svelte Root layout. Deliberately minimal, SveltePress
wraps this in the theme's GlobalLayout.
+layout.ts Turns on prerendering and trailing-slash URLs.
+layout.server.ts Build-time data for every page: the "Last
updated" date and the navbar version.
+page.md Home page.
guide/ Guide chapters, one folder per page
(Foundations, Find Your Pattern, Make It Happen).
patterns/+page.md Pattern Index (bare root).
patterns/<scale>/<slug>/+page.md
Pattern pages, nested by Scale/classification.
Add new ones the same way: a folder with a
+page.md inside it.
retrofits/+page.md Retrofit Strategy Index (bare root).
retrofits/<scale>/<slug>/+page.md
Retrofit Strategy entries, nested by Scale.
references/+page.md
media/+page.md Media Gallery: filterable/sortable index of
external videos, podcasts, and articles.
blog/ Dynamic index (BlogIndex.svelte) plus one
folder per post.
search/+page.md The full-page search (also opens as a dialog
from the header). A "utility" page: see below.
about/ Overview, Site Map, The Commonway System, Brand
Guide, Release History (changelog and live
Roadmap), Contributing, Governance, Reporting,
Acknowledgments, and the Terms, Privacy, AI, and
Accessibility policies.
about/sitemap/ The human site map, built from the sidebar and
every page at build time (+page.server.ts).
llms.txt/+server.ts Generates llms.txt from every +page.md's
frontmatter at build time. See below.
robots.txt/+server.ts Blanket-allow robots.txt, also build-time.
sitemap.xml/+server.ts The XML sitemap for search engines.
lib/
site.ts SITE_URL and shared link helpers.
thumbs.ts The naming rule for small image copies.
server/content.ts Reads every +page.md's frontmatter at build
time (llms.txt, sitemap, page images).
server/sitemap-tree.ts Builds the human site map.
data/media/ Media Gallery items: one small TypeScript
file per item in entries/, schema in types.ts.
theme/ The theme itself. You will rarely need to
touch this once the visual direction is set.
index.ts Theme entry point (the SveltePress contract).
config.ts TypeScript shape of the theme's options.
layout.ts Svelte stores (sidebar open/closed, dark mode).
display-settings.ts The Display settings panel's saved choices.
search.ts Client side of site search (loads the index).
search-meta.ts Search filter values (section, pattern type).
highlighter.ts Code block syntax highlighting (Shiki).
schema/ JSON-LD structured data, built per page.
markdown/ admonitions.ts (:::note etc.), heading-anchors.ts,
external-links.ts, table-scroll.ts.
styles/
tokens.css *** Brand control panel. Colors, type, spacing,
all as CSS variables. Start here for any
visual change. ***
base.css Base element styles and content typography,
built from the tokens above, plus the Display
settings modes.
components/ Layout shell (GlobalLayout, PageLayout, Navbar,
Sidebar, Toc, TocMobile, PageNav), search
(SearchButton, SearchDialog, SearchPanel), the
Display settings panel, the site map, the
Commonway-specific components (Pattern and
Retrofit cards, Citation, EvidenceChip,
DensityChip, GuidanceTable, the Speed & Modal
Hierarchy card, Illustration, Media Gallery
pieces, homepage sections), and the Brand Guide
preview components. One file per component;
index.ts lists the ones pages can import.
scripts/ Build helpers run by `pnpm run build`:
generate-text-pages.mjs (text-only and markdown
versions), build-search-index.mjs (search),
make-thumbs.mjs (`pnpm run thumbs`).
static/ Files served as-is: logos, favicons, diagrams,
pattern illustrations (patterns/), media
previews (media-assets/), and the small copies
in each thumbs/ folder. _redirects and
_headers are Netlify's own rules files.
vite.config.ts Publication content lives here: navbar links, the
manual sidebar tree, GitHub link, edit-link
template. This is the file you edit most.
(This tree reflects what the publication's own changelog at
/about/release-history/ records as built. If you're checking exact file
names or paths for something you're about to edit, confirm against the
actual repo, this section is a map, not a guarantee.)
If you're only here to edit page content, not code, you mainly need the "Editing content" section right below, not the rest of this structure.
Every page is a +page.md file with YAML frontmatter:
---
title: Page Title
description: Optional, used for the meta description tag.
llms: Optional, a one-sentence plain-language summary used in llms.txt.
llmsOptional: false # true lists this page under llms.txt's "## Optional" section instead
date: 2026-01-31 # first publish date, used in the page's structured data
image: /patterns/example.png # optional featured image, see "Adding images" below
---
Regular markdown. Tables, code blocks, and GitHub-flavored markdown
(checklists, strikethrough) all work.The page's <h1> is rendered automatically from title, don't repeat it
as a # Heading in the body, that duplicates it.
llms falls back to description, then title, if omitted. See
"llms.txt, sitemap, and markdown versions" below. Pattern and Retrofit
Strategy pages also carry a patternId (for example LOC-BBG-12). A utility
page such as /search/ sets utility: true, which keeps it out of the search
index, the sitemaps, and the text-only and markdown versions.
Admonition blocks:
:::note
A neutral callout.
:::
:::tip
Positive/encouraged guidance.
:::
:::warning
Caution, e.g. the "Avoid" pattern for stroads.
:::
:::danger
Stronger than warning, same idea.
:::
:::unsourced
Flags a claim that still needs a citation. Always renders in the reserved
amber, regardless of the surrounding content, so it stands out during
drafting and is easy to grep for before publication.
:::Using the CS-specific components inside a page (add the <script> import
at the top of the .md file, SveltePress lets Svelte run inside
markdown). You don't need to understand Svelte deeply to do this, copying
the pattern from an existing page of the same type is normally enough:
<script>
import { PatternCard, DensityChip, Citation } from '$lib/theme/components'
</script>
<PatternCard id="LOC-BBG-12" title="Bicycle Boulevard" classification="local">
One or two sentence summary of the pattern.
</PatternCard>
Applies at: <DensityChip tier="Urban" />
A cited claim.<Citation index={1} />classification accepts local, collector, arterial, freeway,
intersections, network, corridor, facility, or element, and
controls the card's accent color.
A page's featured image is one optional line of frontmatter, image:,
pointing at a file under static/ (for example
image: /patterns/loc-aly-02-alley.png). That single line is what the
Pattern Index and Retrofit Index cards, the search results, the social-share
preview, and the page's structured data all use. A page with no image: shows
a placeholder on its card, and no thumbnail in search unless it is a Pattern,
Retrofit Strategy, blog post, or Media item (those always show one).
The full-size illustrations are large (about 1 MB each), so search results and cards show small copies instead. Whenever you add or replace an image, run:
pnpm run thumbsthen include the new files in the thumbs/ folders next to your image in your
commit. It only makes the copies that are missing or out of date, so it is safe
to run any time. If you forget, pnpm run build warns about the missing copy
and the publication falls back to the full-size image, which works but loads
slowly.
Media Gallery previews are saved with the publication (static/media-assets/),
never loaded from another website's servers.
Open vite.config.ts. The commonwayTheme({...}) call at the top is the
single source of truth for:
navbar: top nav linkssidebar: manual sidebar, keyed by route prefix (e.g. everything under/guide/uses the'/guide/'entry). Each entry is{ title, to }for a link, or{ title, items, collapsible }for a group. Nest groups as deep as you need.github,editLink,footerNote,siteTitle,logoText
The sidebar shown for any page is whichever key in sidebar the current URL
starts with, same idea as SveltePress's own default theme.
Everything visual is a CSS custom property in
src/lib/theme/styles/tokens.css. In particular:
--cw-local,--cw-collector,--cw-arterial,--cw-freeway: the per-Functional-Classification colors used on Pattern ID cards and sidebar chrome, each backed by a full 6-tier density ramp (--cw-local-undevelopedthrough--cw-local-core, etc.) synced to the real Brand Guide palette. Every Scale prefix has its own real ramp: Network, Corridor, Local, Collector, Arterial, Freeway, Intersections & Crossings, Facility, and Element (nine in all, with hues placed in spectrum order and checked for colorblind separation; the colored text label beside every use is what carries the classification, never the color alone).--cw-amber/--cw-amber-soft/--cw-amber-ink: the reserved highlight color. Used only for pattern highlighting and the:::unsourcedflag, never as a general UI accent, by design.--cw-font-display/--cw-font-body: Fraunces and Inter, loaded as self-hosted variable fonts via@fontsource-variable/*(no external font CDN).- The
[data-theme='dark']block at the bottom of the file is the dark-mode override for every token above.
src/routes/llms.txt/+server.ts, src/routes/robots.txt/+server.ts, and
src/routes/sitemap.xml/+server.ts are SvelteKit endpoints, not static files.
llms.txt and sitemap.xml are built from every +page.md's frontmatter
(llms, falling back to description, then title) at build time, via
src/lib/server/content.ts, so a new page appears in both automatically,
nothing to hand-maintain. llms.txt groups pages into ## sections by their
top-level route folder (/guide/ becomes "Guide", etc.); set llmsOptional: true
on a page's frontmatter to list it under the spec's reserved ## Optional
heading instead, for lower-priority stub/placeholder pages a context-constrained
reader can skip. robots.txt is a blanket allow, since the whole guidebook is
public.
Every page also has a markdown version at the same path with .md on the
end (/patterns/foo/ becomes /patterns/foo.md; the home page is
/index.md), and build/llms-full.txt holds the whole guidebook as one
markdown file. Both are written by scripts/generate-text-pages.mjs from the
already-built HTML, so components' content is included. llms.txt links to the
.md versions, and each page's <head> advertises its own. static/_headers
serves them as markdown and asks search engines not to index them.
Because adapter-static prerenders everything ahead of time, the endpoints
compile down to plain files in build/, same as any other route, no server
required at runtime. Nothing on the publication links to some of them, so they
are listed explicitly in svelte.config.js's kit.prerender.entries, otherwise
the prerender crawler would never find them. The canonical domain used for
absolute URLs lives in src/lib/site.ts (SITE_URL).
Search is built on Pagefind: the index is built at
the end of pnpm run build (scripts/build-search-index.mjs) and runs entirely
in the browser, so nothing a reader types is sent anywhere. Every page's main
content is indexed, plus one result per Media Gallery item. It opens from the
Search button in the header (or Ctrl/Cmd+K) and as the full page at /search/.
The human-readable site map at /about/sitemap/ is generated at build time from
the navbar, the sidebar tree in vite.config.ts, and every page. A page that
is missing from the sidebar still appears, under "Other pages", and the build
prints a warning so you can add it to the sidebar.
.github/PULL_REQUEST_TEMPLATE.md and .github/ISSUE_TEMPLATE/*.yml back
the checklists and report categories described on /about/contributing/
and /about/reporting/; edit those pages and these templates together if
the process changes. CODE_OF_CONDUCT.md at the repo root backs the "Code
of conduct" sections on /about/contributing/ and /about/governance/.
If you're new to contributing and not sure where to start beyond getting
this running, /about/contributing/ is the canonical walkthrough for the
actual submission process, that's the page to check first, rather than
this file, for anything about the contribution process itself.
Two different licenses cover two different things in this repository, since GitHub's own license detector only surfaces one license per repo by default:
- Publication code (Svelte components, theme configuration, build tooling, everything except the guidebook content itself): MIT, matching SveltePress.
- Guidebook content (the pattern pages and prose under
src/routes/**/+page.md, and the compiled Commonway System guidebook): licensed separately, not MIT and not a fully open license like Creative Commons. See the "Terms of contributing" section on/about/contributing/for the model, an entity-retains-rights, contributor-grants-a-license arrangement, the same way a publisher retains rights to a book that many people contributed research to.
This project's feature roadmap lives in one place: the live Roadmap
section on /about/release-history/
(or /about/release-history/ on your own local copy once pnpm run dev
is running). It's kept current there, not duplicated here, since a second
static list in this file would drift out of sync with the real one.