Skip to content

Your first template

This walkthrough takes you from an empty directory to a template zip that the Port60 Studio can accept. You do not need the Port60 platform repository or a platform account to build and validate the template locally.

  • Node 20 or newer
  • npm

Templates contain Liquid and CSS only. The kit supplies the preview renderer, contract fixtures and the same conformance checks used when you upload a template.

Run the published kit with npx:

Terminal window
npx @port60/template-kit@latest create my-template
cd my-template
npm install

The folder name becomes the template’s name, and the label derives from it, pass --name or --label only when the identity should differ from the folder (the name is your immutable catalogue identity, name@version, in the store and marketplace). The scaffold pins the kit you scaffolded with as a devDependency, so npm install puts you on the current contract tooling. The scaffold includes npm scripts plus AGENTS.md and CLAUDE.md, so a coding agent receives the same rules as a human contributor.

Your new project contains:

my-template/
├── manifest.json
├── layout.liquid
├── sections/
├── assets/
│ └── theme.css
├── AGENTS.md
└── package.json

manifest.json is the template’s identity and capability declaration. Keep name lowercase and hyphenated. Change label and description to the wording a non profit will see in the picker.

Terminal window
npm run dev

Open http://localhost:4400. The preview renders the contract’s sample content, including a worship schedule when the template declares supports.worship. Functional islands appear as realistic, non-interactive skeletons using their published styling classes. Payments, identity, consent and other live behaviour remain platform owned, and the preview makes no network requests.

Files are read again on refresh. Keep the preview open while you edit layout.liquid, sections/*.liquid and assets/theme.css.

Most of the design lives in two places:

  • assets/theme.css controls colour, typography, spacing, responsive layout and the documented styling classes for platform islands.
  • sections/*.liquid controls the markup for each supported section type.

Keep outputs escaped. Use | raw only for a contract field documented as sanitised rich text:

<section class="page-hero">
{% if section.eyebrow %}<p>{{ section.eyebrow }}</p>{% endif %}
<h1>{{ section.title }}</h1>
{% if section.bodyHtml %}
<div>{{ section.bodyHtml | raw }}</div>
{% endif %}
</section>

Navigation, worship times, donations and sign in are all supplied by the platform. Render their documented data or place their islands. Do not replace them with fixed sample content.

Terminal window
npm run validate:json

The command checks the manifest, restricted Liquid dialect, minimal and sample fixtures, island declarations, layout slot and capability claims. A successful result includes the capabilities the validator has proven:

{
"ok": true,
"errors": [],
"warnings": [],
"provenSupports": {
"layout": true,
"worship": true
}
}

Treat every error as an upload blocker. Review warnings too, particularly a missing member_menu, because member enabled sites would otherwise lose their sign in route.

Terminal window
npm run package

The kit validates once more and creates <name>-<version>.zip with only the allowed artifact files. Upload that zip in your Studio, open the template, choose Versions, then submit the validated version for review.

Published versions are immutable. Increase the semantic version in manifest.json before you package the next release. Existing sites stay on their pinned version until they deliberately apply an update.