Page templates & display data
A Port60 template can own several independent presentation surfaces. Each surface is opt in and the platform keeps responsibility for data fetching, transactions, identity and consent.
The four presentation surfaces
Section titled “The four presentation surfaces”| Manifest declaration | What the template owns | Required file |
|---|---|---|
supports.layout: true |
Header, navigation and footer around every public page | layout.liquid |
supports.pages: ["home", "about"] |
The section based body of the named page | One renderer in sections/ for each supported section type |
supports.pageTemplates: ["events", "course", "articles", "article"] |
A documented platform data view, with the route specific ownership described below | The corresponding file in pages/ |
Capability flags such as supports.worship |
A documented optional context used by the declared surface | The renderer that consumes the context |
These declarations do not imply one another. A page template requires layout because its body is inserted into the layout content slot. A template may own layout without owning any page template, and it may support only some section based pages.
Route ownership
Section titled “Route ownership”| Route or view | Template owned body when declared | Platform owned body |
|---|---|---|
/ |
Home sections when supports.pages contains home |
Built in home renderers |
/about |
About sections when supports.pages contains about |
Built in about renderers |
/events |
pages/events.liquid when supports.pageTemplates contains events |
Built in event listing |
| Event detail | Never | Detail, RSVP and ticket purchase |
/courses |
Never | Course listing |
| Course detail | pages/course.liquid when supports.pageTemplates contains course |
Built in detail when not declared, with enrolment still handled by an island |
/articles and /articles/all |
pages/articles.liquid when declared |
Built in editorial front page and archive |
/articles/{slug} |
pages/article.liquid when declared |
Built in article detail when not declared; engagement and comments remain islands |
| Donations, services, members and prayer times | Never in contract v1 | Platform body inside the template layout |
The platform always owns the document head, consent controls, identity flows and transactional behaviour. Your layout supplies the visible chrome around these bodies.
Declaring a page template
Section titled “Declaring a page template”{ "supports": { "pageTemplates": ["events"] } }Ship pages/events.liquid with that declaration. The platform fetches the data, renders your
markup with the documented context, then inserts the result into your layout. Without the
declaration, the platform body renders inside the same layout.
The data is curated, not raw
Section titled “The data is curated, not raw”A page template receives the page’s documented context objects. See
Render context. It never receives internal API responses. The events
listing receives events, a stable view containing fields such as name, times, venue, mode,
availability and priceFrom. Article listings receive articles and categories. Article details
receive article, categories and moreArticles.
Two habits are important:
- Branch on nullable fields.
description,venueName,imageUrlandpriceFromare null when absent. - Use
detailHreffor links. Routing belongs to the platform, so templates must not construct detail URLs.
{% for e in events %} <article> <h3><a href="{{ e.detailHref }}">{{ e.name }}</a></h3> <p>{{ e.startsAt | date: "%A %-d %B, %H:%M" }}{% if e.venueName %} · {{ e.venueName }}{% endif %}</p> {% if e.soldOut %}<span>Sold out</span>{% elsif e.priceFrom %}<span>from £{{ e.priceFrom }}</span>{% endif %} </article>{% endfor %}The conformance validator renders each page template against its fixture states. Publication fails when a supported state cannot render.
Listing and detail ownership
Section titled “Listing and detail ownership”The four page templates deliberately own different views:
eventsowns the event listing. Event details and every RSVP or ticket transaction remain platform owned.courseowns a course detail presentation. The course listing remains platform owned, and the enrolment action is provided by thecourse_enrolisland.articlesowns the article front page and archive listings. Use the suppliedhrefvalues for articles and category filters.articleowns the article detail presentation. The sanitisedarticle.bodyHtmlis the only article field intended for| raw; placearticle_engagementandarticle_commentsfor the platform-owned interactive parts.
This is a display contract, not a route takeover. The manifest reference and generated context reference are the source of truth as new views are introduced additively.

