@port60/template-kit 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -1
- package/package.json +1 -1
- package/src/commands/create.mjs +1 -1
- package/src/lib/agentsMd.mjs +58 -1
- package/src/vendor/contract/v2/content-model.json +1 -1
- package/src/vendor/contract/v2/dialect.json +3 -1
- package/src/vendor/contract/v2/islands.json +1 -1
- package/src/vendor/contract/v2/manifest.schema.json +49 -0
- package/src/vendor/contract/v2/sections.json +219 -1
- package/src/vendor/contract/v2/site.schema.json +4 -0
- package/src/vendor/contract/v2.lock.json +334 -4
- package/src/vendor/engine/dialect.mjs +4 -0
- package/src/vendor/engine/locale.mjs +74 -0
- package/src/vendor/engine/presentation-capabilities.mjs +45 -0
- package/src/vendor/engine/section-heading-alignment.mjs +28 -0
- package/src/vendor/validator/collection-link-visibility.mjs +47 -0
- package/src/vendor/validator/heading-alignment.mjs +148 -0
- package/src/vendor/validator/navigation-modes.mjs +60 -0
- package/src/vendor/validator/platform-base.css +106 -11
- package/src/vendor/validator/presentation-proof.mjs +53 -0
- package/src/vendor/validator/preview-v2.mjs +9 -2
- package/src/vendor/validator/site-context-v2.mjs +17 -2
- package/src/vendor/validator/validate-v2.mjs +127 -3
- package/starter/manifest.json +1 -0
package/README.md
CHANGED
|
@@ -50,11 +50,53 @@ See the [AI quickstart](https://developers.port60.com/guides/ai-quickstart/).
|
|
|
50
50
|
|
|
51
51
|
## The contract
|
|
52
52
|
|
|
53
|
-
Kit 1.
|
|
53
|
+
Kit 1.2.0 authors **port60-liquid@2**, content model **2.0**, using the contract under
|
|
54
54
|
`src/vendor/contract/v2`. Collection envelopes, independent header/footer navigation,
|
|
55
55
|
resolved actions and page-scoped sections are explicit. V1 sources need a deliberate migration,
|
|
56
56
|
not a manifest-only relabel. Historical v1 contracts remain frozen for existing platform pins.
|
|
57
57
|
|
|
58
|
+
### Optional editor controls in 1.2.0
|
|
59
|
+
|
|
60
|
+
The editor only offers controls supported by the exact selected template version. Declare
|
|
61
|
+
and implement these capabilities together; the validator checks the rendered result:
|
|
62
|
+
|
|
63
|
+
- `supports.sectionHeadingAlignment`: supported section types and logical `start`, `center`
|
|
64
|
+
and `end` values. Put `data-p60-heading-align="{{ section.headingAlignment }}"` on the real
|
|
65
|
+
heading/eyebrow group. Move only that group within existing readable-width containers and
|
|
66
|
+
gutters at desktop widths of 1024px and above; preserve default and mobile layouts.
|
|
67
|
+
- `supports.navigationModes`: explicit Simple/Mega options and a default, with
|
|
68
|
+
`data-p60-navigation-mode="{{ site.nav.headerMode }}"` on the real header menu. Both modes
|
|
69
|
+
preserve the supplied menu tree and destinations. Do not infer support from highlights.
|
|
70
|
+
- `supports.sectionCollectionLinkVisibility`: sections whose existing onward collection link
|
|
71
|
+
has `data-p60-collection-link`. Keep that anchor in the DOM and add `hidden` only when
|
|
72
|
+
`section.showCollectionLink == false`, so the editor can reveal it without a page reload.
|
|
73
|
+
- `supports.heroImageLimit`: an integer from 1 to 6 alongside `supports.heroImagery: true`.
|
|
74
|
+
The limit must reflect actual rendering, not just a carousel declaration. Missing means
|
|
75
|
+
one photograph; no imagery support means none. Stored photographs are never truncated.
|
|
76
|
+
Hidden markup and unrelated CSS URL text cannot prove multi-photo capacity.
|
|
77
|
+
|
|
78
|
+
The compatible host must be deployed before these templates are published. Controls without
|
|
79
|
+
declared support remain unavailable, and older pinned versions keep their existing behaviour.
|
|
80
|
+
The visitor language switch remains dormant pending a separate multilingual-content flow;
|
|
81
|
+
organisation language, RTL/LTR support and interface translation remain available.
|
|
82
|
+
Generated AGENTS.md and CLAUDE.md contain the detailed authoring contract and validation loop.
|
|
83
|
+
|
|
84
|
+
### Added in 1.1.0
|
|
85
|
+
|
|
86
|
+
The v2 dialect adds `t` and `local_date`, matching the compatible platform host. Use
|
|
87
|
+
`t: site.locale.code` only for supported interface phrases, not tenant-authored content.
|
|
88
|
+
The initial phrase catalogue is Arabic; unknown phrases or languages preserve the supplied
|
|
89
|
+
text. `local_date` formats English, Welsh or Arabic Gregorian dates in UTC with fixed
|
|
90
|
+
`date` and `datetime` styles. It does not convert the tenant's prayer wall-clock times.
|
|
91
|
+
|
|
92
|
+
Schedule rows can carry stable `key` values for prayer icons, independently of their supplied
|
|
93
|
+
display names. Shared mobile island styling also uses logical offsets for both directions.
|
|
94
|
+
Historical v1 filters and contracts are unchanged. These additions do not imply complete
|
|
95
|
+
Arabic live-island, typography or legal-copy support.
|
|
96
|
+
|
|
97
|
+
Deploy the compatible host before uploading templates that use the new filters. Kit 1.0.0
|
|
98
|
+
will correctly reject them. See the [localisation guide](https://developers.port60.com/guides/localisation/).
|
|
99
|
+
|
|
58
100
|
## Designer previews
|
|
59
101
|
|
|
60
102
|
The scaffold creates `preview/config.json` and `preview/media/`. Optional `content` points to
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@port60/template-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Build Port60 site templates locally: scaffold, live-preview, validate against the platform contract, and package for studio upload. AI-agent ready: every scaffold ships AGENTS.md and validate emits machine-readable JSON.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/create.mjs
CHANGED
|
@@ -60,7 +60,7 @@ export function create(args) {
|
|
|
60
60
|
}, null, 2) + '\n');
|
|
61
61
|
writeFileSync(join(target, 'README.md'), `# ${label}
|
|
62
62
|
|
|
63
|
-
A Port60 site template using \`port60-liquid@2\`, content model \`2.0\` and kit
|
|
63
|
+
A Port60 site template using \`port60-liquid@2\`, content model \`2.0\` and kit \`${KIT_PACKAGE.version}\`.
|
|
64
64
|
Existing v1 platform pins keep historical support; this kit accepts v2 authoring only.
|
|
65
65
|
Start with \`npm install\`, then:
|
|
66
66
|
|
package/src/lib/agentsMd.mjs
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
|
|
3
|
+
const KIT_VERSION = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version;
|
|
4
|
+
|
|
1
5
|
// A contract briefing shared by every generated coding-agent instruction file.
|
|
2
6
|
export function agentsMd(name) {
|
|
3
7
|
return `# Working on the "${name}" Port60 template
|
|
@@ -8,7 +12,7 @@ identity, authored content and inline editing markers while changing presentatio
|
|
|
8
12
|
|
|
9
13
|
## Versions and iteration
|
|
10
14
|
|
|
11
|
-
Use format port60-liquid@2, content
|
|
15
|
+
Use format port60-liquid@2, content model 2.0 and kit ${KIT_VERSION}. Existing v1 platform pins retain
|
|
12
16
|
their historical contract; this kit explicitly rejects v1 for new authoring and uploads.
|
|
13
17
|
Never relabel v1 without migrating its reads. Published name/version identities are immutable.
|
|
14
18
|
|
|
@@ -68,6 +72,30 @@ Use nil checks, not Liquid default, wherever clearing has meaning. Keep generate
|
|
|
68
72
|
of raw section content. Mark an authored title only in the nonempty override branch. An inherited
|
|
69
73
|
heading has no data-p60-field marker.
|
|
70
74
|
|
|
75
|
+
Heading alignment is an optional per-section capability, not a global theme setting. Declare
|
|
76
|
+
supports.sectionHeadingAlignment as a map, for example {"services":["start","center","end"]}.
|
|
77
|
+
Each key must also be in supports.sections; list only the nonempty, unique subset you implement.
|
|
78
|
+
Place data-p60-heading-align="{{ section.headingAlignment }}" on the actual h1 to h6 heading,
|
|
79
|
+
its eyebrow, or a wrapper containing only that heading and eyebrow. The heading itself must be
|
|
80
|
+
inside/on a matching hook; do not mark the whole section, cards, media, body copy or actions.
|
|
81
|
+
Omit section.headingAlignment for the template's authored default. Never use a Liquid default
|
|
82
|
+
filter or a hardcoded active hook value: absent/unsupported values must leave the design alone.
|
|
83
|
+
Keep the empty hook on every visible heading at the default too, so the first editor choice
|
|
84
|
+
can update immediately. Only omit the heading and its hook together when that copy is hidden.
|
|
85
|
+
The platform applies start/center/end text alignment only at desktop widths (1024px and above).
|
|
86
|
+
Template CSS may position only the heading group within its existing template-owned content
|
|
87
|
+
container and gutters. Preserve its readable width; never widen it to the viewport, remove the
|
|
88
|
+
gutters or move the whole hero copy block. A full-bleed image can stay full bleed independently.
|
|
89
|
+
Introductions, body copy, buttons, cards, images and donation widgets keep their placement and
|
|
90
|
+
alignment. Scope local group-placement rules to that template's heading hook, explicit active
|
|
91
|
+
values and the same desktop breakpoint; do not introduce global margins or a new layout setting.
|
|
92
|
+
Default and mobile keep the template layout. Start/end follow text direction, including RTL.
|
|
93
|
+
Keep field markers and text unchanged; validate every advertised value and visually check every
|
|
94
|
+
Look. Prove visible heading alignment and bounded group placement with readable width retained,
|
|
95
|
+
unchanged surrounding content, reset and mobile; computed text-align alone is not visual proof.
|
|
96
|
+
This capability needs a coordinated host/kit release; keep existing published dependency pins
|
|
97
|
+
until that supporting kit version is available.
|
|
98
|
+
|
|
71
99
|
Declare supports.fieldMarkers:true when showing authored field markers. A marker such as
|
|
72
100
|
data-p60-field="title" or data-p60-field="items.{{ forloop.index0 }}.label" addresses only that
|
|
73
101
|
section's content. Its node must contain exactly the authored value. Use a span when punctuation
|
|
@@ -75,6 +103,13 @@ or generated text surrounds it. Never mark source records, generated labels or r
|
|
|
75
103
|
|
|
76
104
|
## Navigation and actions
|
|
77
105
|
|
|
106
|
+
Optional supports.navigationModes is {options:["simple","mega"],default:"mega"} (either supported
|
|
107
|
+
mode may be the default). It requires supports.layout and both working presentations. Mark the
|
|
108
|
+
header navigation root data-p60-navigation-mode="{{ site.nav.headerMode }}". The host resolves the
|
|
109
|
+
saved choice or declared default; missing capability keeps the historical layout. Do not infer
|
|
110
|
+
support from navigationHighlights. Both modes retain supplied destinations, node order and saved
|
|
111
|
+
metadata. Scope mode CSS to that marker and verify mobile, keyboard and no-JavaScript navigation.
|
|
112
|
+
|
|
78
113
|
site.nav.header and site.nav.footer are independent arrays. kind link has href; kind group has
|
|
79
114
|
null href and children. Use disclosure controls for groups, not fake links. Render two child
|
|
80
115
|
levels and preserve description, imageUrl and optional megaMenu.promo. No derived menus, CTA
|
|
@@ -85,6 +120,14 @@ site.actions.header and site.actions.hero are resolved actions or null. site.act
|
|
|
85
120
|
donate, volunteer or none. Do not infer actions from navigation. Authored hero override text
|
|
86
121
|
retains its field marker; resolved fallback actions do not.
|
|
87
122
|
|
|
123
|
+
Optional supports.sectionCollectionLinkVisibility lists supported collection section types with
|
|
124
|
+
an existing onward link. Mark only that anchor data-p60-collection-link, keep its supplied href
|
|
125
|
+
and existing label, and add hidden only when section.showCollectionLink == false. Missing/true
|
|
126
|
+
keeps the default link. Keep the hidden anchor for immediate editor updates; omit it only when
|
|
127
|
+
there is no supplied destination. Do not hide headings, cards or individual record links, invent
|
|
128
|
+
URLs, or add text/position controls. Unsupported saved preferences are retained but not rendered.
|
|
129
|
+
Both presentation capabilities need a coordinated host/kit release, not a speculative registry pin.
|
|
130
|
+
|
|
88
131
|
## Safety and design
|
|
89
132
|
|
|
90
133
|
- Output is escaped. Use raw only for contract-sanitised richtext.
|
|
@@ -97,7 +140,21 @@ retains its field marker; resolved fallback actions do not.
|
|
|
97
140
|
- Declare only what you render, and render what you declare. Capability matching is metadata,
|
|
98
141
|
not a transfer of route ownership or tenant entitlements.
|
|
99
142
|
- Hero photos need a palette scrim, a carousel for multiple photos and a designed no-photo state.
|
|
143
|
+
Declare supports.heroImagery:true plus supports.heroImageLimit as an integer from 1 to 6
|
|
144
|
+
matching the homeHero's actual capacity. Missing limit means one photograph; no hero imagery
|
|
145
|
+
means zero. A limit above one must render every distinct photograph in a fixture of that
|
|
146
|
+
size or place the hero_carousel island. A generic carousel declaration is not proof.
|
|
147
|
+
The editor reads the exact selected template version, never the catalogue's latest version.
|
|
148
|
+
Saved photographs are never truncated on template switches. Publish a new immutable version
|
|
149
|
+
with this declaration only after the matching host and kit release; preserve existing pins.
|
|
100
150
|
- Preserve settings and Looks. Use platform fonts and declared CSS tokens.
|
|
151
|
+
- The host owns lang/dir and Arabic fonts. Use logical CSS properties. Read site.locale.code.
|
|
152
|
+
- Do not add a visitor language selector. site.locale.languages is currently empty and the
|
|
153
|
+
legacy language_switch slot renders nothing. Interface catalogues are not translated tenant content.
|
|
154
|
+
- The next coordinated kit release adds t for supported interface phrases and local_date for
|
|
155
|
+
fixed Gregorian date/datetime formatting in UTC. Do not use these on registry kit 1.0.0.
|
|
156
|
+
Never translate authored text, infer prayer identities from translated names or convert
|
|
157
|
+
supplied prayer wall-clock strings. Details: /guides/localisation/ in the developer docs.
|
|
101
158
|
- Scope behaviour-dependent hidden content under .p60-js so no-JavaScript stays readable.
|
|
102
159
|
- Keep loops bounded. Test empty collections, cleared text and unknown enum values.
|
|
103
160
|
|
|
@@ -746,7 +746,7 @@
|
|
|
746
746
|
"since": "2.0",
|
|
747
747
|
"cap": 4,
|
|
748
748
|
"moreHref": null,
|
|
749
|
-
"description": "TYPE-DERIVED recurring schedules. `type` is an OPEN enum; 'worship' is the first (
|
|
749
|
+
"description": "TYPE-DERIVED recurring schedules. `type` is an OPEN enum; 'worship' is the first (times, labels, today, next, jumah). Mosque daily times and next may carry a stable `key`: fajr, zuhr, asr, maghrib or isha. Use key for icons, never replace the supplied name. Missing/unknown keys remain readable without an icon. Friday rows are `jumah`, not `jumuah`. Other institutions arrive as new types; render the types you know, ignore the rest.",
|
|
750
750
|
"item": {
|
|
751
751
|
"type": {
|
|
752
752
|
"type": "string",
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
"name": "language_switch",
|
|
141
141
|
"label": "Language switch",
|
|
142
142
|
"status": "available",
|
|
143
|
-
"description": "
|
|
143
|
+
"description": "Temporarily inactive: currently renders nothing. Localisation supports a site's language and text direction; translated tenant content and an explicit per-site language choice need a separate opt-in design. The host supplies locale and direction independently. Do not add a replacement visitor selector.",
|
|
144
144
|
"params": [],
|
|
145
145
|
"stylingApi": [
|
|
146
146
|
".language-switch",
|
|
@@ -91,6 +91,12 @@
|
|
|
91
91
|
"pages",
|
|
92
92
|
"sections"
|
|
93
93
|
],
|
|
94
|
+
"dependentSchemas": {
|
|
95
|
+
"heroImageLimit": {
|
|
96
|
+
"required": ["heroImagery"],
|
|
97
|
+
"properties": { "heroImagery": { "const": true } }
|
|
98
|
+
}
|
|
99
|
+
},
|
|
94
100
|
"properties": {
|
|
95
101
|
"pages": {
|
|
96
102
|
"type": "array",
|
|
@@ -151,6 +157,30 @@
|
|
|
151
157
|
"default": false,
|
|
152
158
|
"description": "True when the layout renders the `worship` context (the prayer/service-times rail). Declared honesty: the choosers badge worship-enabled tenants toward looks that show their times, and warn on looks that don't. Requires supports.layout; the layout must actually branch on `worship`."
|
|
153
159
|
},
|
|
160
|
+
"navigationModes": {
|
|
161
|
+
"type": "object",
|
|
162
|
+
"additionalProperties": false,
|
|
163
|
+
"required": ["options", "default"],
|
|
164
|
+
"properties": {
|
|
165
|
+
"options": {
|
|
166
|
+
"type": "array",
|
|
167
|
+
"minItems": 2,
|
|
168
|
+
"maxItems": 2,
|
|
169
|
+
"uniqueItems": true,
|
|
170
|
+
"items": { "enum": ["simple", "mega"] }
|
|
171
|
+
},
|
|
172
|
+
"default": { "enum": ["simple", "mega"] }
|
|
173
|
+
},
|
|
174
|
+
"description": "Explicit header presentation switch for this exact layout. Both simple and mega must be implemented without changing saved navigation nodes, destinations or metadata. Requires supports.layout. The host resolves an accepted saved menus.headerMode or this default into site.nav.headerMode. Mark the navigation root data-p60-navigation-mode with that resolved mode. Missing support preserves the historical layout and leaves saved preferences unused, never inferred from navigationHighlights."
|
|
175
|
+
},
|
|
176
|
+
"sectionCollectionLinkVisibility": {
|
|
177
|
+
"type": "array",
|
|
178
|
+
"minItems": 1,
|
|
179
|
+
"maxItems": 7,
|
|
180
|
+
"uniqueItems": true,
|
|
181
|
+
"items": { "enum": ["events", "articles", "courses", "campaigns", "services", "documents", "appealGrid"] },
|
|
182
|
+
"description": "Supported collection section types whose existing onward link may be hidden. Every entry must also be in supports.sections and render its supplied collection destination on a permanent data-p60-collection-link anchor. Only section.showCollectionLink == false adds hidden; absent or true retains the template default link. Keep its shared label and destination unchanged, and never hide the heading, records, cards or individual record links. No new URLs, text override or positioning control."
|
|
183
|
+
},
|
|
154
184
|
"navigationHighlights": {
|
|
155
185
|
"type": "boolean",
|
|
156
186
|
"default": false,
|
|
@@ -162,11 +192,30 @@
|
|
|
162
192
|
"description": "True when the sections mark which node shows which field with data-p60-field, so the editor can put the caret on the page instead of in a side panel. The address is inside that section's own content: data-p60-field=\"title\", or data-p60-field=\"items.{{ forloop.index0 }}.label\" for an entry in a list. Purely additive: the attribute is markup, the contract is untouched, and a template that marks nothing keeps the section-level editor. The validator reads the marks from the source and warns on any that does not name a field of that section. Missing or false leaves this exact template version editing in the panel.",
|
|
163
193
|
"$comment": "Follows the navigationHighlights pattern: declared honesty, proven by the validator."
|
|
164
194
|
},
|
|
195
|
+
"sectionHeadingAlignment": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"maxProperties": 16,
|
|
198
|
+
"propertyNames": { "enum": ["hero", "values", "cta", "homeHero", "impact", "events", "articles", "campaigns", "emergency", "stats", "appealGrid", "services", "documents", "people", "impactMap", "courses"] },
|
|
199
|
+
"additionalProperties": {
|
|
200
|
+
"type": "array",
|
|
201
|
+
"minItems": 1,
|
|
202
|
+
"maxItems": 3,
|
|
203
|
+
"uniqueItems": true,
|
|
204
|
+
"items": { "enum": ["start", "center", "end"] }
|
|
205
|
+
},
|
|
206
|
+
"description": "Explicit opt-in per supported section type to desktop heading alignment choices. Missing entries disable the editor control. The host projects section.headingAlignment only for declared options. Mark heading/eyebrow elements using data-p60-heading-align with that value. The shared base applies text-align at widths of at least 1024 CSS pixels; absent/empty hooks retain the template default at every width. The validator proves each declared option renders on real headings and cannot change unrelated content. No layout positioning is implied."
|
|
207
|
+
},
|
|
165
208
|
"heroImagery": {
|
|
166
209
|
"type": "boolean",
|
|
167
210
|
"default": false,
|
|
168
211
|
"description": "True when the homeHero renderer displays the tenant's hero photographs (homeHero.images, legacy imageUrl), always under a TREATMENT built from the template's own palette variables (scrim/tint/blend), never raw. Declared honesty, proven behaviourally at validation: the choosers badge photo-led tenants toward templates that show their images and warn on ones that won't. Requires the homeHero section among supports.sections."
|
|
169
212
|
},
|
|
213
|
+
"heroImageLimit": {
|
|
214
|
+
"type": "integer",
|
|
215
|
+
"minimum": 1,
|
|
216
|
+
"maximum": 6,
|
|
217
|
+
"description": "Maximum homeHero photographs this exact template version can present. Requires supports.heroImagery: true. Missing means one; no hero imagery means zero. Values above one require rendering every image in a fixture of the declared size or placing the hero_carousel island. Generic carousel behavior or island declarations alone do not opt in. This gates editor additions, never truncates saved photographs or changes an older artifact's rendering."
|
|
218
|
+
},
|
|
170
219
|
"behaviors": {
|
|
171
220
|
"type": "array",
|
|
172
221
|
"items": {
|
|
@@ -9,6 +9,17 @@
|
|
|
9
9
|
],
|
|
10
10
|
"description": "The page opener: a headline with an optional kicker and sanitised rich-text intro.",
|
|
11
11
|
"fields": [
|
|
12
|
+
{
|
|
13
|
+
"name": "headingAlignment",
|
|
14
|
+
"kind": "select",
|
|
15
|
+
"required": false,
|
|
16
|
+
"options": [
|
|
17
|
+
"start",
|
|
18
|
+
"center",
|
|
19
|
+
"end"
|
|
20
|
+
],
|
|
21
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
22
|
+
},
|
|
12
23
|
{
|
|
13
24
|
"name": "eyebrow",
|
|
14
25
|
"kind": "text",
|
|
@@ -45,6 +56,17 @@
|
|
|
45
56
|
],
|
|
46
57
|
"description": "A set of principles shown as cards or a numbered list, per the template.",
|
|
47
58
|
"fields": [
|
|
59
|
+
{
|
|
60
|
+
"name": "headingAlignment",
|
|
61
|
+
"kind": "select",
|
|
62
|
+
"required": false,
|
|
63
|
+
"options": [
|
|
64
|
+
"start",
|
|
65
|
+
"center",
|
|
66
|
+
"end"
|
|
67
|
+
],
|
|
68
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
69
|
+
},
|
|
48
70
|
{
|
|
49
71
|
"name": "eyebrow",
|
|
50
72
|
"kind": "text",
|
|
@@ -120,6 +142,17 @@
|
|
|
120
142
|
],
|
|
121
143
|
"description": "A closing band: heading, optional line, one button.",
|
|
122
144
|
"fields": [
|
|
145
|
+
{
|
|
146
|
+
"name": "headingAlignment",
|
|
147
|
+
"kind": "select",
|
|
148
|
+
"required": false,
|
|
149
|
+
"options": [
|
|
150
|
+
"start",
|
|
151
|
+
"center",
|
|
152
|
+
"end"
|
|
153
|
+
],
|
|
154
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
155
|
+
},
|
|
123
156
|
{
|
|
124
157
|
"name": "heading",
|
|
125
158
|
"kind": "text",
|
|
@@ -165,6 +198,17 @@
|
|
|
165
198
|
],
|
|
166
199
|
"description": "The home opener. EVERY field optional: eyebrow/title derive from branding when absent, the lead OMITS when absent (never a fabricated placeholder), buttons fall back to platform defaults.",
|
|
167
200
|
"fields": [
|
|
201
|
+
{
|
|
202
|
+
"name": "headingAlignment",
|
|
203
|
+
"kind": "select",
|
|
204
|
+
"required": false,
|
|
205
|
+
"options": [
|
|
206
|
+
"start",
|
|
207
|
+
"center",
|
|
208
|
+
"end"
|
|
209
|
+
],
|
|
210
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
211
|
+
},
|
|
168
212
|
{
|
|
169
213
|
"name": "eyebrow",
|
|
170
214
|
"kind": "text",
|
|
@@ -227,7 +271,7 @@
|
|
|
227
271
|
"name": "images",
|
|
228
272
|
"kind": "items",
|
|
229
273
|
"required": false,
|
|
230
|
-
"description": "Hero photographs (0-6, uploaded through the platform pipeline). One = a main image; several = rotation material
|
|
274
|
+
"description": "Hero photographs (0-6, uploaded through the platform pipeline). One = a main image; several = rotation material. A template with supports.heroImagery renders at least the first and may place the hero_carousel island (or a CSS scroll strip) for the rest. Declare supports.heroImageLimit above one to offer multiple photographs in the editor; missing means one, and absent heroImagery means zero. Saved photographs survive template switches and are never truncated by this capability. Like every field: absent means the template's designed no-photo state, never a placeholder.",
|
|
231
275
|
"itemFields": [
|
|
232
276
|
{
|
|
233
277
|
"name": "imageUrl",
|
|
@@ -295,6 +339,17 @@
|
|
|
295
339
|
],
|
|
296
340
|
"description": "What gifts make possible, optional intro plus icon/title/description cards. Claims must be truthful for the tenant.",
|
|
297
341
|
"fields": [
|
|
342
|
+
{
|
|
343
|
+
"name": "headingAlignment",
|
|
344
|
+
"kind": "select",
|
|
345
|
+
"required": false,
|
|
346
|
+
"options": [
|
|
347
|
+
"start",
|
|
348
|
+
"center",
|
|
349
|
+
"end"
|
|
350
|
+
],
|
|
351
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
352
|
+
},
|
|
298
353
|
{
|
|
299
354
|
"name": "eyebrow",
|
|
300
355
|
"kind": "text",
|
|
@@ -384,6 +439,23 @@
|
|
|
384
439
|
"kind": "widget",
|
|
385
440
|
"description": "Published events across registration modes. Render the supplied bounded site.content.events.items with this placement's authored introduction. Optional motion uses declared behaviours; ticket sales and RSVP stay on the platform-owned detail route supplied by each item.",
|
|
386
441
|
"fields": [
|
|
442
|
+
{
|
|
443
|
+
"name": "showCollectionLink",
|
|
444
|
+
"kind": "boolean",
|
|
445
|
+
"required": false,
|
|
446
|
+
"description": "Only false hides this placement's existing onward collection link. Missing or true keeps the template default. Requires this exact template to declare supports.sectionCollectionLinkVisibility for this section. Keep a stable data-p60-collection-link anchor and add hidden only for false. The platform-supplied URL and shared label stay unchanged; headings, records and individual card links are unaffected."
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"name": "headingAlignment",
|
|
450
|
+
"kind": "select",
|
|
451
|
+
"required": false,
|
|
452
|
+
"options": [
|
|
453
|
+
"start",
|
|
454
|
+
"center",
|
|
455
|
+
"end"
|
|
456
|
+
],
|
|
457
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
458
|
+
},
|
|
387
459
|
{
|
|
388
460
|
"name": "eyebrow",
|
|
389
461
|
"kind": "text",
|
|
@@ -422,6 +494,23 @@
|
|
|
422
494
|
"kind": "widget",
|
|
423
495
|
"description": "Published articles from the supplied bounded site.content.articles.items. Render the records with this placement's authored introduction and link through the supplied item URLs. Article engagement and comments remain platform islands on the detail page.",
|
|
424
496
|
"fields": [
|
|
497
|
+
{
|
|
498
|
+
"name": "showCollectionLink",
|
|
499
|
+
"kind": "boolean",
|
|
500
|
+
"required": false,
|
|
501
|
+
"description": "Only false hides this placement's existing onward collection link. Missing or true keeps the template default. Requires this exact template to declare supports.sectionCollectionLinkVisibility for this section. Keep a stable data-p60-collection-link anchor and add hidden only for false. The platform-supplied URL and shared label stay unchanged; headings, records and individual card links are unaffected."
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
"name": "headingAlignment",
|
|
505
|
+
"kind": "select",
|
|
506
|
+
"required": false,
|
|
507
|
+
"options": [
|
|
508
|
+
"start",
|
|
509
|
+
"center",
|
|
510
|
+
"end"
|
|
511
|
+
],
|
|
512
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
513
|
+
},
|
|
425
514
|
{
|
|
426
515
|
"name": "eyebrow",
|
|
427
516
|
"kind": "text",
|
|
@@ -463,6 +552,23 @@
|
|
|
463
552
|
"campaignsLabel"
|
|
464
553
|
],
|
|
465
554
|
"fields": [
|
|
555
|
+
{
|
|
556
|
+
"name": "showCollectionLink",
|
|
557
|
+
"kind": "boolean",
|
|
558
|
+
"required": false,
|
|
559
|
+
"description": "Only false hides this placement's existing onward collection link. Missing or true keeps the template default. Requires this exact template to declare supports.sectionCollectionLinkVisibility for this section. Keep a stable data-p60-collection-link anchor and add hidden only for false. The platform-supplied URL and shared label stay unchanged; headings, records and individual card links are unaffected."
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
"name": "headingAlignment",
|
|
563
|
+
"kind": "select",
|
|
564
|
+
"required": false,
|
|
565
|
+
"options": [
|
|
566
|
+
"start",
|
|
567
|
+
"center",
|
|
568
|
+
"end"
|
|
569
|
+
],
|
|
570
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
571
|
+
},
|
|
466
572
|
{
|
|
467
573
|
"name": "eyebrow",
|
|
468
574
|
"kind": "text",
|
|
@@ -500,6 +606,17 @@
|
|
|
500
606
|
"causes"
|
|
501
607
|
],
|
|
502
608
|
"fields": [
|
|
609
|
+
{
|
|
610
|
+
"name": "headingAlignment",
|
|
611
|
+
"kind": "select",
|
|
612
|
+
"required": false,
|
|
613
|
+
"options": [
|
|
614
|
+
"start",
|
|
615
|
+
"center",
|
|
616
|
+
"end"
|
|
617
|
+
],
|
|
618
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
619
|
+
},
|
|
503
620
|
{
|
|
504
621
|
"name": "heading",
|
|
505
622
|
"kind": "text",
|
|
@@ -543,6 +660,17 @@
|
|
|
543
660
|
],
|
|
544
661
|
"description": "Big headline numbers, the at-a-glance impact band. Values are author-entered text (write them exactly as they should read), or, per item, a live figure the platform counts (`source`).",
|
|
545
662
|
"fields": [
|
|
663
|
+
{
|
|
664
|
+
"name": "headingAlignment",
|
|
665
|
+
"kind": "select",
|
|
666
|
+
"required": false,
|
|
667
|
+
"options": [
|
|
668
|
+
"start",
|
|
669
|
+
"center",
|
|
670
|
+
"end"
|
|
671
|
+
],
|
|
672
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
673
|
+
},
|
|
546
674
|
{
|
|
547
675
|
"name": "eyebrow",
|
|
548
676
|
"kind": "text",
|
|
@@ -626,6 +754,23 @@
|
|
|
626
754
|
"causes"
|
|
627
755
|
],
|
|
628
756
|
"fields": [
|
|
757
|
+
{
|
|
758
|
+
"name": "showCollectionLink",
|
|
759
|
+
"kind": "boolean",
|
|
760
|
+
"required": false,
|
|
761
|
+
"description": "Only false hides this placement's existing onward collection link. Missing or true keeps the template default. Requires this exact template to declare supports.sectionCollectionLinkVisibility for this section. Keep a stable data-p60-collection-link anchor and add hidden only for false. The platform-supplied URL and shared label stay unchanged; headings, records and individual card links are unaffected."
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
"name": "headingAlignment",
|
|
765
|
+
"kind": "select",
|
|
766
|
+
"required": false,
|
|
767
|
+
"options": [
|
|
768
|
+
"start",
|
|
769
|
+
"center",
|
|
770
|
+
"end"
|
|
771
|
+
],
|
|
772
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
773
|
+
},
|
|
629
774
|
{
|
|
630
775
|
"name": "eyebrow",
|
|
631
776
|
"kind": "text",
|
|
@@ -715,6 +860,23 @@
|
|
|
715
860
|
"services"
|
|
716
861
|
],
|
|
717
862
|
"fields": [
|
|
863
|
+
{
|
|
864
|
+
"name": "showCollectionLink",
|
|
865
|
+
"kind": "boolean",
|
|
866
|
+
"required": false,
|
|
867
|
+
"description": "Only false hides this placement's existing onward collection link. Missing or true keeps the template default. Requires this exact template to declare supports.sectionCollectionLinkVisibility for this section. Keep a stable data-p60-collection-link anchor and add hidden only for false. The platform-supplied URL and shared label stay unchanged; headings, records and individual card links are unaffected."
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
"name": "headingAlignment",
|
|
871
|
+
"kind": "select",
|
|
872
|
+
"required": false,
|
|
873
|
+
"options": [
|
|
874
|
+
"start",
|
|
875
|
+
"center",
|
|
876
|
+
"end"
|
|
877
|
+
],
|
|
878
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
879
|
+
},
|
|
718
880
|
{
|
|
719
881
|
"name": "eyebrow",
|
|
720
882
|
"kind": "text",
|
|
@@ -752,6 +914,23 @@
|
|
|
752
914
|
"resources"
|
|
753
915
|
],
|
|
754
916
|
"fields": [
|
|
917
|
+
{
|
|
918
|
+
"name": "showCollectionLink",
|
|
919
|
+
"kind": "boolean",
|
|
920
|
+
"required": false,
|
|
921
|
+
"description": "Only false hides this placement's existing onward collection link. Missing or true keeps the template default. Requires this exact template to declare supports.sectionCollectionLinkVisibility for this section. Keep a stable data-p60-collection-link anchor and add hidden only for false. The platform-supplied URL and shared label stay unchanged; headings, records and individual card links are unaffected."
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
"name": "headingAlignment",
|
|
925
|
+
"kind": "select",
|
|
926
|
+
"required": false,
|
|
927
|
+
"options": [
|
|
928
|
+
"start",
|
|
929
|
+
"center",
|
|
930
|
+
"end"
|
|
931
|
+
],
|
|
932
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
933
|
+
},
|
|
755
934
|
{
|
|
756
935
|
"name": "eyebrow",
|
|
757
936
|
"kind": "text",
|
|
@@ -786,6 +965,17 @@
|
|
|
786
965
|
],
|
|
787
966
|
"description": "The key people behind the charity, trustees, staff, volunteers, as simple cards: name, position, an optional photograph (uploaded through the platform pipeline) and an optional one-line statement. Templates render a card grid that collapses to a single column on small screens; a card without a photo renders cleanly (initials or plain), never a placeholder face.",
|
|
788
967
|
"fields": [
|
|
968
|
+
{
|
|
969
|
+
"name": "headingAlignment",
|
|
970
|
+
"kind": "select",
|
|
971
|
+
"required": false,
|
|
972
|
+
"options": [
|
|
973
|
+
"start",
|
|
974
|
+
"center",
|
|
975
|
+
"end"
|
|
976
|
+
],
|
|
977
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
978
|
+
},
|
|
789
979
|
{
|
|
790
980
|
"name": "eyebrow",
|
|
791
981
|
"kind": "text",
|
|
@@ -878,6 +1068,17 @@
|
|
|
878
1068
|
"description": "The organisation's impact map: a themed map whose points link existing content (campaigns, causes, articles, events, places), each opening a card derived from the linked thing. Place the `map` island, it brings the map, the points and the cards; your template brings placement and tokens (the island theming contract).",
|
|
879
1069
|
"context": [],
|
|
880
1070
|
"fields": [
|
|
1071
|
+
{
|
|
1072
|
+
"name": "headingAlignment",
|
|
1073
|
+
"kind": "select",
|
|
1074
|
+
"required": false,
|
|
1075
|
+
"options": [
|
|
1076
|
+
"start",
|
|
1077
|
+
"center",
|
|
1078
|
+
"end"
|
|
1079
|
+
],
|
|
1080
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
1081
|
+
},
|
|
881
1082
|
{
|
|
882
1083
|
"name": "eyebrow",
|
|
883
1084
|
"kind": "text",
|
|
@@ -917,6 +1118,23 @@
|
|
|
917
1118
|
"home"
|
|
918
1119
|
],
|
|
919
1120
|
"fields": [
|
|
1121
|
+
{
|
|
1122
|
+
"name": "showCollectionLink",
|
|
1123
|
+
"kind": "boolean",
|
|
1124
|
+
"required": false,
|
|
1125
|
+
"description": "Only false hides this placement's existing onward collection link. Missing or true keeps the template default. Requires this exact template to declare supports.sectionCollectionLinkVisibility for this section. Keep a stable data-p60-collection-link anchor and add hidden only for false. The platform-supplied URL and shared label stay unchanged; headings, records and individual card links are unaffected."
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
"name": "headingAlignment",
|
|
1129
|
+
"kind": "select",
|
|
1130
|
+
"required": false,
|
|
1131
|
+
"options": [
|
|
1132
|
+
"start",
|
|
1133
|
+
"center",
|
|
1134
|
+
"end"
|
|
1135
|
+
],
|
|
1136
|
+
"description": "Desktop alignment of the heading and optional eyebrow. The template may position their heading-only group within its existing content container and gutters while preserving readable width. Only applied when this exact template declares supports.sectionHeadingAlignment for this section and option. Absent means template default. Start/end follow the site direction. Below 1024 CSS pixels the template retains its own layout. Introductions, body copy, buttons, cards, images and islands keep their placement and alignment; full-bleed imagery does not widen the heading bounds."
|
|
1137
|
+
},
|
|
920
1138
|
{
|
|
921
1139
|
"name": "eyebrow",
|
|
922
1140
|
"kind": "text",
|
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
"footer"
|
|
53
53
|
],
|
|
54
54
|
"properties": {
|
|
55
|
+
"headerMode": {
|
|
56
|
+
"enum": ["simple", "mega"],
|
|
57
|
+
"description": "Resolved header presentation only when the exact template declares navigationModes. The host uses the accepted saved preference or the declared default. Omitted for unsupported templates; never inferred from menu shape or highlights."
|
|
58
|
+
},
|
|
55
59
|
"header": {
|
|
56
60
|
"type": "array",
|
|
57
61
|
"items": {
|