@port60/template-kit 0.7.0 → 0.9.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.
@@ -78,7 +78,7 @@
78
78
  "availableIn": [
79
79
  "layout"
80
80
  ],
81
- "description": "Optional sub-items (e.g. a services dropdown, or a tenant-curated 'About us' group). Absent on plain links; when present, render a dropdown/expandable group. Children are { label, href, external? } — one level only."
81
+ "description": "Optional sub-items for dropdowns and mega menus. Children use the same item shape and may carry one further children level, giving a maximum of two levels below the top item."
82
82
  },
83
83
  {
84
84
  "path": "nav.items[].external",
@@ -87,6 +87,34 @@
87
87
  ],
88
88
  "description": "True when the item points off-site (a tenant-curated external link) — add target=\"_blank\" rel=\"noopener\". Absent/false for internal links."
89
89
  },
90
+ {
91
+ "path": "nav.items[].group",
92
+ "availableIn": [
93
+ "layout"
94
+ ],
95
+ "description": "Optional tenant-authored column or topic label for grouping items inside a mega menu."
96
+ },
97
+ {
98
+ "path": "nav.items[].description",
99
+ "availableIn": [
100
+ "layout"
101
+ ],
102
+ "description": "Optional short supporting text for a richer navigation card."
103
+ },
104
+ {
105
+ "path": "nav.items[].imageUrl",
106
+ "availableIn": [
107
+ "layout"
108
+ ],
109
+ "description": "Optional image URL for a navigation card. Always branch when absent."
110
+ },
111
+ {
112
+ "path": "nav.items[].megaMenu",
113
+ "availableIn": [
114
+ "layout"
115
+ ],
116
+ "description": "Optional mega-menu presentation metadata on a top-level item: columns (2 to 4) and an optional promo object with title, text, href, label and imageUrl. It is content only; the template owns hover, focus and responsive presentation in CSS."
117
+ },
90
118
  {
91
119
  "path": "nav.footer",
92
120
  "availableIn": [
@@ -185,6 +213,54 @@
185
213
  ],
186
214
  "description": "Today in the tenant's alternate calendar (Islamic civil calendar, e.g. \"11 Safar 1448\") when they opted in, else null — always branch."
187
215
  },
216
+ {
217
+ "path": "worship.next",
218
+ "availableIn": [
219
+ "layout"
220
+ ],
221
+ "description": "The next prayer or service as {name, begins, congregation, target}, or null. target is an ISO timestamp in the organisation's timezone. Templates may display it statically; use the next_prayer island for a ticking countdown."
222
+ },
223
+ {
224
+ "path": "worship.jumah",
225
+ "availableIn": [
226
+ "layout"
227
+ ],
228
+ "description": "Structured Friday congregations as rows of {name, begins, congregation}. Empty for schedules without configured Friday times."
229
+ },
230
+ {
231
+ "path": "worship.hijri",
232
+ "availableIn": [
233
+ "layout"
234
+ ],
235
+ "description": "Structured alternate-calendar data. Contract v1 exposes {display}, or null when disabled."
236
+ },
237
+ {
238
+ "path": "worship.observances",
239
+ "availableIn": [
240
+ "layout"
241
+ ],
242
+ "description": "Upcoming structured observances as {name, date, href}. Empty until the tenant has authored observances."
243
+ },
244
+ {
245
+ "path": "locale",
246
+ "availableIn": ["layout"],
247
+ "description": "The public rendering locale and direction. Always present as {code, direction, languages}."
248
+ },
249
+ {
250
+ "path": "locale.code",
251
+ "availableIn": ["layout"],
252
+ "description": "Current BCP 47 language code, currently en, cy or ar."
253
+ },
254
+ {
255
+ "path": "locale.direction",
256
+ "availableIn": ["layout"],
257
+ "description": "ltr or rtl. The platform also applies this direction to the document element."
258
+ },
259
+ {
260
+ "path": "locale.languages",
261
+ "availableIn": ["layout"],
262
+ "description": "Supported public languages as {code, label, direction}. Place the language_switch island for platform-owned switching behaviour."
263
+ },
188
264
  {
189
265
  "path": "events",
190
266
  "availableIn": [
@@ -193,6 +269,66 @@
193
269
  ],
194
270
  "description": "The tenant's published events, soonest first — a CURATED read-only view for display. On page:events it is every published event; on section:events it is the TICKETED ones (the section's purpose). Transactional steps (RSVP, ticket purchase) stay platform-owned on the detail view — link with detailHref, never rebuild those flows."
195
271
  },
272
+ {
273
+ "path": "causes",
274
+ "availableIn": [
275
+ "section:appealGrid",
276
+ "section:emergency"
277
+ ],
278
+ "description": "Published and currently available appeals. This is a display-only summary; payment processor identifiers and transaction configuration are never exposed. Empty when there is no active appeal."
279
+ },
280
+ {
281
+ "path": "causes[]",
282
+ "availableIn": [
283
+ "section:appealGrid",
284
+ "section:emergency"
285
+ ],
286
+ "description": "An appeal card with id, title, slug, description, imageUrl, targetAmount, raisedAmount, progressPercentage, href and a boolean paymentOptions summary. Always use href to reach the platform donation flow."
287
+ },
288
+ {
289
+ "path": "services",
290
+ "availableIn": [
291
+ "section:programmes"
292
+ ],
293
+ "description": "Published services and programmes in display order. Empty when none are published, so omit the section rather than inventing cards."
294
+ },
295
+ {
296
+ "path": "services[]",
297
+ "availableIn": [
298
+ "section:programmes"
299
+ ],
300
+ "description": "A service card with id, title, slug, summary, imageUrl and href. Use href rather than constructing a route."
301
+ },
302
+ {
303
+ "path": "resources",
304
+ "availableIn": ["section:resources"],
305
+ "description": "Public documents from the tenant's media library, newest first. Private assets are never included. Empty when no public documents exist."
306
+ },
307
+ {
308
+ "path": "resources[]",
309
+ "availableIn": ["section:resources"],
310
+ "description": "A safe document link with id, title, folder, url, contentType, bytes and publishedAt. Always use url rather than constructing storage paths."
311
+ },
312
+ {
313
+ "path": "locations",
314
+ "availableIn": ["section:locations"],
315
+ "description": "Public locations from the tenant's venue book, primary first. Empty when no venues are configured."
316
+ },
317
+ {
318
+ "path": "locations[]",
319
+ "availableIn": ["section:locations"],
320
+ "description": "A public location with id, name, address, latitude, longitude, primary and directionsHref. Provider references and private notes are excluded."
321
+ },
322
+ {
323
+ "path": "opportunities",
324
+ "availableIn": ["section:volunteering"],
325
+ "description": "Published volunteering opportunities in display order. Each item contains id, slug, title, summary, imageUrl and content with location, schedule, commitment, dates, expected hours and shift dates. Sign-up remains the volunteer_signup island."
326
+ },
327
+ {
328
+ "path": "media",
329
+ "availableIn": ["section:media"],
330
+ "description": "Published structured media entries. Each item contains id, slug, title, summary, imageUrl and content with mediaType, mediaUrl, speaker, publishedAt and duration."
331
+ },
196
332
  {
197
333
  "path": "infoEvents",
198
334
  "availableIn": [
@@ -256,6 +392,84 @@
256
392
  ],
257
393
  "description": "The article's category names (possibly empty) — treat as text."
258
394
  },
395
+ {
396
+ "path": "campaigns",
397
+ "availableIn": [
398
+ "section:campaigns"
399
+ ],
400
+ "description": "The tenant's live campaigns, in display order. Empty when none are live — render nothing."
401
+ },
402
+ {
403
+ "path": "campaigns[].title",
404
+ "availableIn": [
405
+ "section:campaigns"
406
+ ],
407
+ "description": "The campaign's name."
408
+ },
409
+ {
410
+ "path": "campaigns[].href",
411
+ "availableIn": [
412
+ "section:campaigns"
413
+ ],
414
+ "description": "The canonical link to the campaign page — always use this."
415
+ },
416
+ {
417
+ "path": "campaigns[].summary",
418
+ "availableIn": [
419
+ "section:campaigns"
420
+ ],
421
+ "description": "Short summary, or null — branch on it."
422
+ },
423
+ {
424
+ "path": "campaigns[].imageUrl",
425
+ "availableIn": [
426
+ "section:campaigns"
427
+ ],
428
+ "description": "Featured image URL, or null — always branch."
429
+ },
430
+ {
431
+ "path": "campaignsLabel",
432
+ "availableIn": [
433
+ "section:campaigns"
434
+ ],
435
+ "description": "What the tenant calls their campaigns section (\"Campaigns\" by default) — the heading fallback."
436
+ },
437
+ {
438
+ "path": "articles",
439
+ "availableIn": [
440
+ "page:articles"
441
+ ],
442
+ "description": "Published article summaries for the current listing or selected category. Each item contains id, title, slug, excerpt, imageUrl, authorName, authorRole, categories, publishedAt, readingMinutes, engagement counts and href."
443
+ },
444
+ {
445
+ "path": "selectedCategory",
446
+ "availableIn": [
447
+ "page:articles"
448
+ ],
449
+ "description": "The active category filter, or null on an unfiltered listing."
450
+ },
451
+ {
452
+ "path": "article",
453
+ "availableIn": [
454
+ "page:article"
455
+ ],
456
+ "description": "The article detail being displayed. It carries the article summary fields plus sanitised bodyHtml, updatedAt and allowComments. Render bodyHtml with the raw filter; engagement and comments remain platform islands."
457
+ },
458
+ {
459
+ "path": "relatedArticles",
460
+ "availableIn": [
461
+ "page:article"
462
+ ],
463
+ "description": "Up to three other published article summaries, newest first."
464
+ },
465
+ {
466
+ "path": "categories",
467
+ "availableIn": [
468
+ "page:articles",
469
+ "page:article"
470
+ ],
471
+ "description": "Categories currently used by published articles. Each item has name, count and href."
472
+ },
259
473
  {
260
474
  "path": "events[].id",
261
475
  "availableIn": [
@@ -493,13 +707,31 @@
493
707
  "children": [
494
708
  {
495
709
  "label": "Food bank",
496
- "href": "/services?service=food-bank"
710
+ "href": "/services?service=food-bank",
711
+ "group": "Support",
712
+ "description": "Emergency food parcels and practical advice."
497
713
  },
498
714
  {
499
- "label": "Youth club",
500
- "href": "/services?service=youth-club"
715
+ "label": "Community",
716
+ "href": "#",
717
+ "group": "Get involved",
718
+ "children": [
719
+ {
720
+ "label": "Youth club",
721
+ "href": "/services?service=youth-club"
722
+ }
723
+ ]
501
724
  }
502
- ]
725
+ ],
726
+ "megaMenu": {
727
+ "columns": 3,
728
+ "promo": {
729
+ "title": "Visit this week",
730
+ "text": "Find a programme and meet the team.",
731
+ "href": "/services",
732
+ "label": "Explore services"
733
+ }
734
+ }
503
735
  },
504
736
  {
505
737
  "label": "Donate",
@@ -565,7 +797,30 @@
565
797
  "note": "Jum'a 13:15 & 14:15",
566
798
  "href": "/services?service=prayer-times",
567
799
  "today": "24 July 2026",
568
- "todayAlt": "11 Safar 1448"
800
+ "todayAlt": "11 Safar 1448",
801
+ "next": {
802
+ "name": "Asr",
803
+ "begins": "18:30",
804
+ "congregation": "19:00",
805
+ "target": "2030-07-24T19:00:00+01:00"
806
+ },
807
+ "jumah": [
808
+ { "name": "Jum'a 1", "begins": null, "congregation": "13:15" },
809
+ { "name": "Jum'a 2", "begins": null, "congregation": "14:15" }
810
+ ],
811
+ "hijri": { "display": "11 Safar 1448" },
812
+ "observances": [
813
+ { "name": "Community iftar", "date": "2 March 2030", "href": "/events" }
814
+ ]
815
+ },
816
+ "locale": {
817
+ "code": "en",
818
+ "direction": "ltr",
819
+ "languages": [
820
+ { "code": "en", "label": "English", "direction": "ltr" },
821
+ { "code": "cy", "label": "Cymraeg", "direction": "ltr" },
822
+ { "code": "ar", "label": "العربية", "direction": "rtl" }
823
+ ]
569
824
  }
570
825
  },
571
826
  "pages": {
@@ -638,6 +893,74 @@
638
893
  "soldOut": false,
639
894
  "backHref": "/courses"
640
895
  }
896
+ },
897
+ "articles": {
898
+ "selectedCategory": null,
899
+ "categories": [
900
+ { "name": "News", "count": 4, "href": "/articles/all?category=News" },
901
+ { "name": "Stories", "count": 2, "href": "/articles/all?category=Stories" }
902
+ ],
903
+ "articles": [
904
+ {
905
+ "id": "88888888-8888-8888-8888-888888888888",
906
+ "title": "Fixture Community Garden Opens",
907
+ "slug": "fixture-community-garden-opens",
908
+ "excerpt": "Neighbours came together to create a new shared space.",
909
+ "imageUrl": null,
910
+ "authorName": "Amina Khan",
911
+ "authorRole": "Community Editor",
912
+ "categories": ["News"],
913
+ "publishedAt": "2030-04-12T09:00:00Z",
914
+ "readingMinutes": 4,
915
+ "viewCount": 82,
916
+ "likeCount": 12,
917
+ "commentCount": 3,
918
+ "href": "/articles/fixture-community-garden-opens"
919
+ }
920
+ ]
921
+ },
922
+ "article": {
923
+ "article": {
924
+ "id": "88888888-8888-8888-8888-888888888888",
925
+ "title": "Fixture Community Garden Opens",
926
+ "slug": "fixture-community-garden-opens",
927
+ "excerpt": "Neighbours came together to create a new shared space.",
928
+ "imageUrl": null,
929
+ "authorName": "Amina Khan",
930
+ "authorRole": "Community Editor",
931
+ "categories": ["News"],
932
+ "publishedAt": "2030-04-12T09:00:00Z",
933
+ "updatedAt": "2030-04-12T10:00:00Z",
934
+ "readingMinutes": 4,
935
+ "viewCount": 82,
936
+ "likeCount": 12,
937
+ "commentCount": 3,
938
+ "href": "/articles/fixture-community-garden-opens",
939
+ "bodyHtml": "<h2>A shared place to grow</h2><p>The garden is now open to everyone.</p>",
940
+ "allowComments": true
941
+ },
942
+ "relatedArticles": [
943
+ {
944
+ "id": "99999999-9999-9999-9999-999999999999",
945
+ "title": "Fixture Volunteer Morning",
946
+ "slug": "fixture-volunteer-morning",
947
+ "excerpt": "How local volunteers prepared the garden.",
948
+ "imageUrl": null,
949
+ "authorName": null,
950
+ "authorRole": null,
951
+ "categories": ["Stories"],
952
+ "publishedAt": "2030-04-01T09:00:00Z",
953
+ "readingMinutes": 3,
954
+ "viewCount": 44,
955
+ "likeCount": 7,
956
+ "commentCount": 0,
957
+ "href": "/articles/fixture-volunteer-morning"
958
+ }
959
+ ],
960
+ "categories": [
961
+ { "name": "News", "count": 4, "href": "/articles/all?category=News" },
962
+ { "name": "Stories", "count": 2, "href": "/articles/all?category=Stories" }
963
+ ]
641
964
  }
642
965
  },
643
966
  "sections": {
@@ -722,7 +1045,136 @@
722
1045
  ]
723
1046
  }
724
1047
  ]
1048
+ },
1049
+ "campaigns": {
1050
+ "campaigns": [
1051
+ {
1052
+ "id": "99999999-9999-9999-9999-999999999991",
1053
+ "title": "Fixture Winter Campaign",
1054
+ "href": "/campaigns/fixture-winter-campaign",
1055
+ "summary": "Help keep neighbours warm this winter.",
1056
+ "imageUrl": "/fixture/campaign.jpg"
1057
+ },
1058
+ {
1059
+ "id": "99999999-9999-9999-9999-999999999992",
1060
+ "title": "Fixture Community Survey",
1061
+ "href": "/campaigns/fixture-community-survey",
1062
+ "summary": null,
1063
+ "imageUrl": null
1064
+ }
1065
+ ],
1066
+ "campaignsLabel": "Campaigns"
1067
+ },
1068
+ "emergency": {
1069
+ "causes": [
1070
+ {
1071
+ "id": "55555555-5555-5555-5555-555555555555",
1072
+ "title": "Fixture Emergency Appeal",
1073
+ "slug": "fixture-emergency-appeal",
1074
+ "description": "Urgent food, water and shelter.",
1075
+ "imageUrl": null,
1076
+ "targetAmount": 50000,
1077
+ "raisedAmount": 18750,
1078
+ "progressPercentage": 38,
1079
+ "href": "/donate/fixture-emergency-appeal",
1080
+ "paymentOptions": {
1081
+ "oneOff": true,
1082
+ "recurringCard": true,
1083
+ "directDebit": true
1084
+ }
1085
+ }
1086
+ ]
1087
+ },
1088
+ "appealGrid": {
1089
+ "causes": [
1090
+ {
1091
+ "id": "55555555-5555-5555-5555-555555555555",
1092
+ "title": "Fixture Winter Appeal",
1093
+ "slug": "fixture-winter-appeal",
1094
+ "description": "Warm meals and heating support.",
1095
+ "imageUrl": null,
1096
+ "targetAmount": 20000,
1097
+ "raisedAmount": 12500,
1098
+ "progressPercentage": 63,
1099
+ "href": "/donate/fixture-winter-appeal",
1100
+ "paymentOptions": {
1101
+ "oneOff": true,
1102
+ "recurringCard": true,
1103
+ "directDebit": false
1104
+ }
1105
+ }
1106
+ ]
1107
+ },
1108
+ "programmes": {
1109
+ "services": [
1110
+ {
1111
+ "id": "66666666-6666-6666-6666-666666666666",
1112
+ "title": "Fixture Food Bank",
1113
+ "slug": "fixture-food-bank",
1114
+ "summary": "Practical food support for local households.",
1115
+ "imageUrl": null,
1116
+ "href": "/services?service=fixture-food-bank"
1117
+ },
1118
+ {
1119
+ "id": "77777777-7777-7777-7777-777777777777",
1120
+ "title": "Fixture Youth Club",
1121
+ "slug": "fixture-youth-club",
1122
+ "summary": "Weekly activities for young people.",
1123
+ "imageUrl": null,
1124
+ "href": "/services?service=fixture-youth-club"
1125
+ }
1126
+ ]
1127
+ },
1128
+ "resources": {
1129
+ "resources": [
1130
+ {
1131
+ "id": "88888888-8888-8888-8888-888888888888",
1132
+ "title": "Fixture annual report.pdf",
1133
+ "folder": "Reports",
1134
+ "url": "/fixture/annual-report.pdf",
1135
+ "contentType": "application/pdf",
1136
+ "bytes": 245760,
1137
+ "publishedAt": "2030-02-01T10:00:00Z"
1138
+ }
1139
+ ]
1140
+ },
1141
+ "locations": {
1142
+ "locations": [
1143
+ {
1144
+ "id": "99999999-9999-9999-9999-999999999999",
1145
+ "name": "Fixture Community Hall",
1146
+ "address": "1 Fixture Street, Leeds",
1147
+ "latitude": 53.8008,
1148
+ "longitude": -1.5491,
1149
+ "primary": true,
1150
+ "directionsHref": "https://www.google.com/maps/dir/?api=1&destination=Fixture%20Community%20Hall"
1151
+ }
1152
+ ]
1153
+ },
1154
+ "volunteering": {
1155
+ "opportunities": [
1156
+ {
1157
+ "id": "aaaaaaaa-1111-4111-8111-111111111111",
1158
+ "slug": "fixture-garden-volunteer",
1159
+ "title": "Fixture Garden Volunteer",
1160
+ "summary": "Help care for the community garden on Saturday mornings.",
1161
+ "imageUrl": null,
1162
+ "content": { "location": "Fixture Garden", "schedule": "Saturday mornings", "commitment": "Twice a month", "hoursExpected": "3 hours" }
1163
+ }
1164
+ ]
1165
+ },
1166
+ "media": {
1167
+ "media": [
1168
+ {
1169
+ "id": "bbbbbbbb-2222-4222-8222-222222222222",
1170
+ "slug": "fixture-community-talk",
1171
+ "title": "Fixture Community Talk",
1172
+ "summary": "A short update from the project team.",
1173
+ "imageUrl": null,
1174
+ "content": { "mediaType": "VIDEO", "mediaUrl": "/fixture/community-talk", "speaker": "Amina Khan", "duration": "18 minutes" }
1175
+ }
1176
+ ]
725
1177
  }
726
1178
  }
727
1179
  }
728
- }
1180
+ }
@@ -91,6 +91,51 @@
91
91
  ".course-soldout-note"
92
92
  ]
93
93
  },
94
+ {
95
+ "name": "next_prayer",
96
+ "label": "Next worship time",
97
+ "status": "available",
98
+ "description": "A live countdown to worship.next for the tenant's schedule. Renders nothing when no next time is available. The platform owns the clock; the template owns presentation through the stable classes.",
99
+ "params": [],
100
+ "stylingApi": [
101
+ ".next-prayer",
102
+ ".next-prayer-label",
103
+ ".next-prayer-name",
104
+ ".next-prayer-time",
105
+ ".next-prayer-countdown"
106
+ ]
107
+ },
108
+ {
109
+ "name": "article_engagement",
110
+ "label": "Article engagement",
111
+ "status": "available",
112
+ "description": "The platform-owned article views, likes and sharing bar for the article page being rendered.",
113
+ "params": [],
114
+ "stylingApi": [
115
+ ".article-engage",
116
+ ".article-views",
117
+ ".article-share-group",
118
+ ".article-share-btn",
119
+ ".article-share-word",
120
+ ".article-share-copied"
121
+ ]
122
+ },
123
+ {
124
+ "name": "article_comments",
125
+ "label": "Article comments",
126
+ "status": "available",
127
+ "description": "The identity-aware article conversation for the article page being rendered. Renders the closed state when comments are disabled.",
128
+ "params": [],
129
+ "stylingApi": [
130
+ ".article-comments",
131
+ ".article-comment-list",
132
+ ".article-comment",
133
+ ".article-comment-head",
134
+ ".article-comment-form",
135
+ ".article-comments-gate",
136
+ ".article-comments-note"
137
+ ]
138
+ },
94
139
  {
95
140
  "name": "hero_carousel",
96
141
  "label": "Hero image carousel",
@@ -101,12 +146,92 @@
101
146
  ".hero-carousel",
102
147
  ".hero-slides",
103
148
  ".hero-slide",
149
+ ".hero-slide--active",
104
150
  ".hero-slide-img",
105
151
  ".hero-slide-scrim",
106
152
  ".hero-dots",
107
153
  ".hero-dot",
108
154
  ".hero-dot--active"
109
155
  ]
156
+ },
157
+ {
158
+ "name": "newsletter_signup",
159
+ "label": "Newsletter sign-up",
160
+ "status": "available",
161
+ "description": "Explicit email opt-in over the platform's versioned consent and one-click unsubscribe rail. Templates own placement and presentation; wording, evidence and abuse checks remain platform-owned.",
162
+ "params": [],
163
+ "stylingApi": [
164
+ ".newsletter-signup",
165
+ ".newsletter-label",
166
+ ".newsletter-fields",
167
+ ".newsletter-email",
168
+ ".newsletter-submit",
169
+ ".newsletter-consent",
170
+ ".newsletter-message"
171
+ ]
172
+ },
173
+ {
174
+ "name": "language_switch",
175
+ "label": "Language switch",
176
+ "status": "available",
177
+ "description": "Switches between the platform's supported public languages while preserving the current route. The host supplies locale and text direction to Liquid and the document element.",
178
+ "params": [],
179
+ "stylingApi": [
180
+ ".language-switch",
181
+ ".language-switch-label",
182
+ ".language-switch-select"
183
+ ]
184
+ },
185
+ {
186
+ "name": "search",
187
+ "label": "Site search",
188
+ "status": "available",
189
+ "description": "Searches published articles, programmes, events and public resources. It reads public projections only and cannot search member, payment, private media or admin data.",
190
+ "params": [],
191
+ "stylingApi": [
192
+ ".site-search",
193
+ ".site-search-form",
194
+ ".site-search-label",
195
+ ".site-search-fields",
196
+ ".site-search-input",
197
+ ".site-search-submit",
198
+ ".site-search-results",
199
+ ".site-search-result",
200
+ ".site-search-kind",
201
+ ".site-search-link",
202
+ ".site-search-summary",
203
+ ".site-search-empty"
204
+ ]
205
+ },
206
+ {
207
+ "name": "volunteer_signup",
208
+ "label": "Volunteer expression of interest",
209
+ "status": "available",
210
+ "description": "Lists published opportunities and captures a privacy-recorded expression of interest. Templates own placement and styling; validation, abuse checks and submission storage remain platform-owned.",
211
+ "params": [],
212
+ "stylingApi": [
213
+ ".volunteer-signup",
214
+ ".volunteer-field",
215
+ ".volunteer-privacy",
216
+ ".volunteer-submit",
217
+ ".volunteer-message"
218
+ ]
219
+ },
220
+ {
221
+ "name": "form",
222
+ "label": "Public form",
223
+ "status": "available",
224
+ "description": "Renders the tenant's first published platform form and records its submission with explicit privacy acceptance. Templates cannot handle or redirect submissions themselves.",
225
+ "params": [],
226
+ "stylingApi": [
227
+ ".public-form",
228
+ ".public-form-head",
229
+ ".public-form-field",
230
+ ".public-form-check",
231
+ ".public-form-privacy",
232
+ ".public-form-submit",
233
+ ".public-form-message"
234
+ ]
110
235
  }
111
236
  ]
112
237
  }