@port60/template-kit 0.8.0 → 0.10.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.
Files changed (40) hide show
  1. package/README.md +10 -3
  2. package/bin/cli.mjs +26 -4
  3. package/package.json +1 -1
  4. package/src/commands/content.mjs +42 -0
  5. package/src/commands/create.mjs +10 -7
  6. package/src/commands/dev.mjs +134 -8
  7. package/src/commands/model.mjs +34 -0
  8. package/src/commands/packageCmd.mjs +3 -3
  9. package/src/commands/refresh.mjs +48 -0
  10. package/src/commands/upgrade.mjs +44 -0
  11. package/src/commands/validate.mjs +2 -3
  12. package/src/lib/agentsMd.mjs +78 -21
  13. package/src/vendor/contract/v1/behaviours.json +129 -0
  14. package/src/vendor/contract/v1/content-bounds.json +3 -3
  15. package/src/vendor/contract/v1/content-model.json +193 -0
  16. package/src/vendor/contract/v1/context.json +1985 -141
  17. package/src/vendor/contract/v1/dialect.json +3 -3
  18. package/src/vendor/contract/v1/fonts.json +1 -1
  19. package/src/vendor/contract/v1/imagery.json +4 -4
  20. package/src/vendor/contract/v1/islands.json +256 -110
  21. package/src/vendor/contract/v1/layout.json +25 -0
  22. package/src/vendor/contract/v1/manifest.schema.json +60 -14
  23. package/src/vendor/contract/v1/sections.json +330 -61
  24. package/src/vendor/contract/v1/tokens.json +1 -1
  25. package/src/vendor/contract/v1.lock.json +1 -1
  26. package/src/vendor/engine/content-footprint.mjs +79 -0
  27. package/src/vendor/validator/behaviors-runtime.js +2 -0
  28. package/src/vendor/validator/fixture-art.mjs +109 -0
  29. package/src/vendor/validator/model-reference.mjs +92 -0
  30. package/src/vendor/validator/platform-base.css +4250 -0
  31. package/src/vendor/validator/preview.mjs +497 -23
  32. package/src/vendor/validator/site-context.mjs +97 -0
  33. package/src/vendor/validator/validate.mjs +214 -14
  34. package/starter/assets/theme.css +54 -8
  35. package/starter/layout.liquid +15 -16
  36. package/starter/manifest.json +10 -5
  37. package/starter/sections/campaigns.liquid +23 -0
  38. package/starter/sections/homeHero.liquid +26 -11
  39. package/starter/sections/people.liquid +1 -1
  40. package/starter/sections/values.liquid +6 -1
@@ -0,0 +1,23 @@
1
+ {% comment %}
2
+ Live site.content.campaigns, the starter's plain rendering of the `site.content.campaigns` context, kept simple on
3
+ purpose: this template is the worked example template authors copy from. The context is the
4
+ platform's published site.content.campaigns; hrefs are platform-derived, always link via campaign.href.
5
+ Derive or omit: nothing renders when no campaign is live.
6
+ {% endcomment %}
7
+ {% if site.content.campaigns.size > 0 %}
8
+ <section class="lq-site.content.campaigns">
9
+ <div class="container">
10
+ <h2>{{ section.heading | default: campaignsLabel }}</h2>
11
+ {% if section.text %}<p>{{ section.text }}</p>{% endif %}
12
+ <ul class="lq-campaign-list">
13
+ {% for campaign in site.content.campaigns %}
14
+ <li class="lq-campaign">
15
+ <h3>{{ campaign.title }}</h3>
16
+ {% if campaign.summary %}<p>{{ campaign.summary }}</p>{% endif %}
17
+ <a class="button button-primary" href="{{ campaign.href }}">View campaign</a>
18
+ </li>
19
+ {% endfor %}
20
+ </ul>
21
+ </div>
22
+ </section>
23
+ {% endif %}
@@ -1,16 +1,16 @@
1
1
  {% comment %}
2
- The home opener — and the REFERENCE IMPLEMENTATION of supports.heroImagery. Rules worth
2
+ The home opener, and the REFERENCE IMPLEMENTATION of supports.heroImagery. Rules worth
3
3
  copying:
4
4
 
5
5
  1. EVERY field is optional. Copy derives from brand (| default) or omits; no photos means the
6
- designed gradient below — never a placeholder image.
6
+ designed gradient below, never a placeholder image.
7
7
  2. Photos are NEVER placed raw. The treatments in theme.css lay scrims built from the palette
8
8
  variables (color-mix over var(--bg)/var(--bg-tint)), so any uploaded photograph is toned
9
9
  into the template. Tune the treatment, don't remove it.
10
10
  3. Rotation is the PLATFORM's hero_carousel island (templates ship no JavaScript): with two or
11
11
  more photos, place it and style its documented classes.
12
12
  4. photoFraming is the charity's call, honour BOTH: 'fill' (default) covers your designed hero
13
- shape (per-photo focus picks the surviving slice); 'whole' is the VISTA — the hero adapts
13
+ shape (per-photo focus picks the surviving slice); 'whole' is the VISTA, the hero adapts
14
14
  toward the photo's own shape (height-clamped) so a wide landscape stays entirely visible,
15
15
  with your copy stacked after the band.
16
16
  {% endcomment %}
@@ -38,8 +38,8 @@
38
38
  {% if heroCount > 1 %}{% island 'hero_carousel' %}{% else %}<div class="lq-vista-scrim" aria-hidden="true"></div>{% endif %}
39
39
  <div class="lq-vista-copy">
40
40
  <div class="container">
41
- <p class="lq-kicker">{{ section.eyebrow | default: brand.name }}</p>
42
- <h1>{{ section.title | default: brand.tagline | default: brand.name }}</h1>
41
+ <p class="lq-kicker">{{ section.eyebrow | default: site.brand.name }}</p>
42
+ <h1>{{ section.title | default: site.brand.tagline | default: site.brand.name }}</h1>
43
43
  {% if section.lead %}<p class="lq-homehero-lead">{{ section.lead }}</p>{% endif %}
44
44
  <div class="lq-homehero-actions">
45
45
  <a class="button button-primary" href="{{ section.primaryHref | default: '/donate' }}">{{ section.primaryLabel | default: 'Donate now' }}</a>
@@ -50,7 +50,7 @@
50
50
  </div>
51
51
  </section>
52
52
  {% else %}
53
- {% comment %} Form mode: the grid pulls up over the band — copy on the photo, the widget riding
53
+ {% comment %} Form mode: the grid pulls up over the band, copy on the photo, the widget riding
54
54
  the seam onto the gradient below (never a stacked strip). {% endcomment %}
55
55
  <section class="lq-homehero lq-homehero--vista lq-homehero--vista-form{% if heroCount > 1 %} lq-homehero--carousel{% endif %}">
56
56
  <div class="lq-vista">
@@ -59,8 +59,8 @@
59
59
  </div>
60
60
  <div class="container lq-homehero-inner lq-vista-overlap">
61
61
  <div>
62
- <p class="lq-kicker">{{ section.eyebrow | default: brand.name }}</p>
63
- <h1>{{ section.title | default: brand.tagline | default: brand.name }}</h1>
62
+ <p class="lq-kicker">{{ section.eyebrow | default: site.brand.name }}</p>
63
+ <h1>{{ section.title | default: site.brand.tagline | default: site.brand.name }}</h1>
64
64
  {% if section.lead %}<p class="lq-homehero-lead">{{ section.lead }}</p>{% endif %}
65
65
  <div class="lq-homehero-actions">
66
66
  <a class="button button-primary" href="#donate">{{ section.primaryLabel | default: 'Donate now' }}</a>
@@ -75,11 +75,26 @@
75
75
  {% endif %}
76
76
  {% else %}
77
77
  <section class="lq-homehero{% if heroPhoto %} lq-homehero--photo{% endif %}{% if heroCount > 1 %} lq-homehero--carousel{% endif %}"{% if heroPhoto %} style="background-image: url('{{ heroPhoto }}'); background-position: {{ heroFocus }}"{% endif %}>
78
- {% if heroCount > 1 %}{% island 'hero_carousel' %}{% endif %}
78
+ {% comment %}
79
+ Several photos → the BEHAVIOUR carousel (data-p60-carousel, engine-wired, the template ships
80
+ no JavaScript): complete static markup, one data-p60-slide per photo, auto-advance opt-in.
81
+ Hiding non-active slides is .p60-js-scoped in theme.css so a no-JS visitor sees the photos
82
+ stacked, never a blank hero. The vista framings above show the other route: the ready-made
83
+ hero_carousel island. A template chooses either.
84
+ {% endcomment %}
85
+ {% if heroCount > 1 %}
86
+ <div class="lq-hero-slides" data-p60-carousel="auto" aria-label="Hero photographs">
87
+ {% for image in section.images %}
88
+ <img class="lq-hero-slide{% if forloop.first %} is-active{% endif %}" data-p60-slide
89
+ src="{{ image.imageUrl }}" alt="{{ image.alt }}" style="object-position: {{ heroFocus }}"
90
+ {% if forloop.first %}fetchpriority="high"{% else %}loading="lazy"{% endif %} decoding="async">
91
+ {% endfor %}
92
+ </div>
93
+ {% endif %}
79
94
  <div class="container lq-homehero-inner">
80
95
  <div>
81
- <p class="lq-kicker">{{ section.eyebrow | default: brand.name }}</p>
82
- <h1>{{ section.title | default: brand.tagline | default: brand.name }}</h1>
96
+ <p class="lq-kicker">{{ section.eyebrow | default: site.brand.name }}</p>
97
+ <h1>{{ section.title | default: site.brand.tagline | default: site.brand.name }}</h1>
83
98
  {% if section.lead %}<p class="lq-homehero-lead">{{ section.lead }}</p>{% endif %}
84
99
  <div class="lq-homehero-actions">
85
100
  {% if section.givingStyle == 'button' %}
@@ -1,5 +1,5 @@
1
1
  {% comment %}
2
- People / team — the reference implementation: simple cards (name, role, optional photo and
2
+ People / team, the reference implementation: simple cards (name, role, optional photo and
3
3
  one-line statement), a grid that collapses to one column on phones. No placeholder faces:
4
4
  a person without a photo just renders text (derive or omit, never fabricate).
5
5
  {% endcomment %}
@@ -1,7 +1,12 @@
1
1
  <section class="lq-values">
2
2
  <div class="container">
3
3
  <h2>{{ section.heading | default: "What guides us" }}</h2>
4
- <ol class="lq-value-rail">
4
+ {% comment %}
5
+ data-p60-reveal-group: the engine stamps --p60-reveal-index on each child and adds
6
+ is-revealed when the rail scrolls into view, the stagger and motion are OURS, in theme.css,
7
+ and .p60-js-scoped so a no-JS visitor sees the rail complete and still.
8
+ {% endcomment %}
9
+ <ol class="lq-value-rail" data-p60-reveal-group>
5
10
  {% for item in section.items %}
6
11
  <li>
7
12
  <span class="lq-value-dot"></span>