@port60/template-kit 0.21.0 → 1.1.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 (55) hide show
  1. package/README.md +58 -3
  2. package/bin/cli.mjs +10 -0
  3. package/package.json +4 -2
  4. package/src/commands/content.mjs +4 -3
  5. package/src/commands/create.mjs +11 -3
  6. package/src/commands/dev.mjs +9 -3
  7. package/src/commands/model.mjs +3 -3
  8. package/src/commands/packageCmd.mjs +1 -1
  9. package/src/commands/publish.mjs +1 -1
  10. package/src/commands/refresh.mjs +8 -3
  11. package/src/commands/release.mjs +22 -0
  12. package/src/commands/setupPreviews.mjs +19 -0
  13. package/src/commands/validate.mjs +1 -1
  14. package/src/lib/agentsMd.mjs +116 -161
  15. package/src/lib/designer-bridge.js +26 -0
  16. package/src/lib/designerPalette.mjs +62 -0
  17. package/src/lib/galleryPosters.mjs +105 -0
  18. package/src/lib/previewOptions.mjs +1 -1
  19. package/src/lib/releaseBundle.mjs +141 -0
  20. package/src/vendor/contract/v2/behaviours.json +346 -0
  21. package/src/vendor/contract/v2/content-bounds.json +117 -0
  22. package/src/vendor/contract/v2/content-model.json +766 -0
  23. package/src/vendor/contract/v2/context.json +1569 -0
  24. package/src/vendor/contract/v2/dialect.json +107 -0
  25. package/src/vendor/contract/v2/fonts.json +910 -0
  26. package/src/vendor/contract/v2/imagery.json +32 -0
  27. package/src/vendor/contract/v2/islands.json +263 -0
  28. package/src/vendor/contract/v2/layout.json +28 -0
  29. package/src/vendor/contract/v2/manifest.schema.json +388 -0
  30. package/src/vendor/contract/v2/sections.json +947 -0
  31. package/src/vendor/contract/v2/site.schema.json +1353 -0
  32. package/src/vendor/contract/v2/tokens.json +101 -0
  33. package/src/vendor/contract/v2.lock.json +6933 -0
  34. package/src/vendor/engine/content-footprint.mjs +76 -1
  35. package/src/vendor/engine/dialect.mjs +4 -0
  36. package/src/vendor/engine/locale.mjs +74 -0
  37. package/src/vendor/engine/majors.mjs +42 -0
  38. package/src/vendor/validator/gift-aid-logo.svg +6 -0
  39. package/src/vendor/validator/model-reference-v2.mjs +95 -0
  40. package/src/vendor/validator/platform-base.css +320 -11
  41. package/src/vendor/validator/preview-v1.mjs +691 -0
  42. package/src/vendor/validator/preview-v2.mjs +654 -0
  43. package/src/vendor/validator/preview.mjs +8 -688
  44. package/src/vendor/validator/site-context-v2.mjs +113 -0
  45. package/src/vendor/validator/validate-v1.mjs +666 -0
  46. package/src/vendor/validator/validate-v2.mjs +642 -0
  47. package/src/vendor/validator/validate.mjs +21 -663
  48. package/starter/assets/theme.css +3 -0
  49. package/starter/layout.liquid +15 -18
  50. package/starter/manifest.json +5 -4
  51. package/starter/preview/config.json +4 -0
  52. package/starter/preview/media/README.md +9 -0
  53. package/starter/sections/campaigns.liquid +5 -4
  54. package/starter/sections/homeHero.liquid +25 -21
  55. package/starter/sections/values.liquid +1 -1
@@ -1064,11 +1064,10 @@ textarea.p60-form-control {
1064
1064
  escapes ANY wrapper depth; layouts here are symmetric, so the sheet lands exactly 0..100vw. */
1065
1065
  @media (max-width: 640px) {
1066
1066
  .donate-card {
1067
- width: 100vw;
1068
- margin-left: calc(50% - 50vw);
1067
+ inline-size: 100vw;
1068
+ margin-inline-start: calc(50% - 50vw);
1069
1069
  border-radius: 0;
1070
- border-left: 0;
1071
- border-right: 0;
1070
+ border-inline: 0;
1072
1071
  padding: 1.4rem 1.1rem;
1073
1072
  }
1074
1073
  }
@@ -1403,6 +1402,232 @@ textarea.p60-form-control {
1403
1402
  color: var(--primary-strong);
1404
1403
  }
1405
1404
 
1405
+ /* The donation island can sit in a narrow desktop column or a wide checkout.
1406
+ Size its fields from their available space, without changing other islands. */
1407
+ .donate-card.donation-widget {
1408
+ padding: 1.25rem;
1409
+ gap: 0.75rem;
1410
+ }
1411
+
1412
+ .donation-widget .donor-fields {
1413
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
1414
+ }
1415
+
1416
+ .donation-widget .giftaid-grid {
1417
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
1418
+ }
1419
+
1420
+ .donation-widget .donor-fields > .marketing-optin,
1421
+ .donation-widget .giftaid-name-grid > .giftaid-title {
1422
+ grid-column: 1 / -1;
1423
+ }
1424
+
1425
+ .donation-widget .giftaid-title {
1426
+ inline-size: min(100%, 10rem);
1427
+ }
1428
+
1429
+ .donation-widget .field,
1430
+ .donation-widget .field input,
1431
+ .donation-widget .field select,
1432
+ .donation-widget .freq-tab {
1433
+ min-inline-size: 0;
1434
+ }
1435
+
1436
+ .donation-widget .field select {
1437
+ inline-size: 100%;
1438
+ border: 1px solid var(--line);
1439
+ border-radius: 12px;
1440
+ padding: 0.85rem 0.95rem;
1441
+ background: var(--surface);
1442
+ color: var(--ink);
1443
+ }
1444
+
1445
+ .donation-widget .field select:focus-visible {
1446
+ outline: 2px solid var(--primary);
1447
+ outline-offset: 2px;
1448
+ }
1449
+
1450
+ .donation-widget .freq-tabs {
1451
+ grid-template-columns: none;
1452
+ grid-auto-flow: column;
1453
+ grid-auto-columns: minmax(0, 1fr);
1454
+ }
1455
+
1456
+ .donation-widget .freq-tab {
1457
+ overflow-wrap: anywhere;
1458
+ }
1459
+
1460
+ .donation-widget .donate-change {
1461
+ white-space: normal;
1462
+ }
1463
+
1464
+ .donation-widget .donation-giftaid-controls {
1465
+ display: grid;
1466
+ gap: 0.5rem;
1467
+ }
1468
+
1469
+ .donation-widget .donation-giftaid-summary {
1470
+ margin: 0;
1471
+ color: var(--ink);
1472
+ font-size: 0.9rem;
1473
+ line-height: 1.5;
1474
+ font-weight: 600;
1475
+ }
1476
+
1477
+ .donation-widget .donation-giftaid-summary-actions {
1478
+ display: flex;
1479
+ flex-wrap: wrap;
1480
+ align-items: center;
1481
+ gap: 0.75rem;
1482
+ }
1483
+
1484
+ /* One button surrounds the action and intact wordmark, preserving its clear space. */
1485
+ .donation-widget .donation-giftaid-trigger {
1486
+ display: inline-flex;
1487
+ align-items: center;
1488
+ justify-content: center;
1489
+ gap: 15px;
1490
+ min-block-size: 44px;
1491
+ padding: 15px;
1492
+ border: 1px solid color-mix(in srgb, var(--ink) 45%, transparent);
1493
+ border-radius: var(--radius);
1494
+ background: transparent;
1495
+ color: var(--ink);
1496
+ font: inherit;
1497
+ font-weight: 600;
1498
+ cursor: pointer;
1499
+ }
1500
+
1501
+ .donation-widget .donation-giftaid-logo {
1502
+ display: block;
1503
+ flex: 0 0 auto;
1504
+ inline-size: 72px;
1505
+ block-size: auto;
1506
+ }
1507
+
1508
+ .donation-widget .donation-giftaid-action {
1509
+ text-decoration: none;
1510
+ }
1511
+
1512
+ .donation-widget .donation-giftaid-trigger:hover:not(:disabled) {
1513
+ border-color: var(--ink);
1514
+ background: color-mix(in srgb, var(--ink) 5%, transparent);
1515
+ }
1516
+
1517
+ .donation-widget .donation-giftaid-trigger:focus-visible {
1518
+ outline: 2px solid var(--ink);
1519
+ outline-offset: 2px;
1520
+ }
1521
+
1522
+ .donation-widget .donation-giftaid-trigger:disabled {
1523
+ opacity: 0.6;
1524
+ cursor: not-allowed;
1525
+ }
1526
+
1527
+ /* Native top layer keeps the optional details clear of template hero clipping and motion. */
1528
+ .donation-widget .donation-giftaid-dialog {
1529
+ position: fixed;
1530
+ inset: 0;
1531
+ margin: auto;
1532
+ inline-size: min(42rem, calc(100% - 2rem));
1533
+ max-inline-size: none;
1534
+ max-block-size: calc(100dvh - 2rem);
1535
+ padding: 0;
1536
+ border: 1px solid var(--line);
1537
+ border-radius: var(--radius);
1538
+ background: var(--surface);
1539
+ color: var(--ink);
1540
+ font: inherit;
1541
+ text-align: start;
1542
+ box-shadow: var(--shadow);
1543
+ overflow: hidden;
1544
+ }
1545
+
1546
+ .donation-widget .donation-giftaid-dialog:not([open]) { display: none; }
1547
+ .donation-widget .donation-giftaid-dialog::backdrop { background: rgb(0 0 0 / 0.5); }
1548
+ html:has(.donation-giftaid-dialog[open]) { overflow: hidden; }
1549
+
1550
+ .donation-widget .donation-giftaid-dialog > form {
1551
+ display: grid;
1552
+ grid-template-rows: auto minmax(0, 1fr) auto;
1553
+ max-block-size: calc(100dvh - 2rem);
1554
+ margin: 0;
1555
+ }
1556
+
1557
+ .donation-widget .donation-giftaid-heading {
1558
+ display: flex;
1559
+ align-items: flex-start;
1560
+ justify-content: space-between;
1561
+ gap: 1rem;
1562
+ padding: 1.5rem;
1563
+ border-block-end: 1px solid var(--line);
1564
+ }
1565
+
1566
+ .donation-widget .donation-giftaid-heading h2 {
1567
+ font-size: clamp(1.25rem, 3vw, 1.6rem);
1568
+ line-height: 1.2;
1569
+ margin: 0;
1570
+ }
1571
+
1572
+ .donation-widget .donation-giftaid-heading p {
1573
+ color: var(--muted);
1574
+ margin: 0.5rem 0 0;
1575
+ font-size: 0.9rem;
1576
+ line-height: 1.5;
1577
+ }
1578
+
1579
+ .donation-widget .donation-giftaid-close {
1580
+ display: grid;
1581
+ place-items: center;
1582
+ flex: none;
1583
+ inline-size: 2.75rem;
1584
+ block-size: 2.75rem;
1585
+ padding: 0;
1586
+ border: 1px solid var(--line-strong);
1587
+ border-radius: 50%;
1588
+ background: var(--surface);
1589
+ color: var(--ink);
1590
+ font: inherit;
1591
+ font-size: 1.5rem;
1592
+ cursor: pointer;
1593
+ }
1594
+
1595
+ .donation-widget .donation-giftaid-body {
1596
+ display: grid;
1597
+ gap: 1.25rem;
1598
+ padding: 1.5rem;
1599
+ min-block-size: 0;
1600
+ overflow-y: auto;
1601
+ overscroll-behavior: contain;
1602
+ }
1603
+
1604
+ .donation-widget .donation-giftaid-dialog .giftaid-declaration { font-size: 0.875rem; line-height: 1.6; }
1605
+
1606
+ .donation-widget .donation-giftaid-actions {
1607
+ display: flex;
1608
+ flex-wrap: wrap;
1609
+ justify-content: flex-end;
1610
+ gap: 0.75rem;
1611
+ padding: 1rem 1.5rem;
1612
+ border-block-start: 1px solid var(--line);
1613
+ background: var(--surface);
1614
+ }
1615
+
1616
+ .donation-widget .donation-giftaid-actions .button { min-block-size: 2.75rem; }
1617
+ .donation-widget .donation-giftaid-dialog :focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
1618
+
1619
+ @media (max-width: 480px) {
1620
+ .donation-widget .donation-giftaid-dialog {
1621
+ inline-size: calc(100% - 1rem);
1622
+ max-block-size: calc(100dvh - 1rem);
1623
+ }
1624
+ .donation-widget .donation-giftaid-dialog > form { max-block-size: calc(100dvh - 1rem); }
1625
+ .donation-widget .donation-giftaid-heading,
1626
+ .donation-widget .donation-giftaid-body,
1627
+ .donation-widget .donation-giftaid-actions { padding: 1rem; }
1628
+ .donation-widget .donation-giftaid-actions .button { flex: 1; }
1629
+ }
1630
+
1406
1631
  .card-host {
1407
1632
  min-height: 2.5rem;
1408
1633
  margin: 0.25rem 0 0.5rem;
@@ -1623,6 +1848,49 @@ textarea.p60-form-control {
1623
1848
  color: var(--ink);
1624
1849
  margin: 0 0 1rem;
1625
1850
  }
1851
+ /* Side by side content from the rich-text editor (articles, events, courses). Structure only, and
1852
+ deliberately unscoped so a template page that renders a body with the raw filter gets the same
1853
+ floor: the theme owns the typography inside a pane and may restyle the gap, the breakpoint or the
1854
+ boxed look. Under 720px of page width the panes stack in reading order. Nothing about width or
1855
+ colour is stored with the content; the classes are exact tokens the sanitiser allow-lists. */
1856
+ .p60-cols {
1857
+ display: grid;
1858
+ gap: 1.5rem;
1859
+ margin: 1.25rem 0;
1860
+ align-items: start;
1861
+ }
1862
+ .p60-cols-2 {
1863
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1864
+ }
1865
+ .p60-cols-3 {
1866
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1867
+ }
1868
+ .p60-col > :first-child {
1869
+ margin-top: 0;
1870
+ }
1871
+ .p60-col > :last-child {
1872
+ margin-bottom: 0;
1873
+ }
1874
+ /* A floated image never escapes its pane. */
1875
+ .p60-col img,
1876
+ .p60-col figure {
1877
+ float: none;
1878
+ max-width: 100%;
1879
+ }
1880
+ /* The one named look: a box drawn from the theme's own tokens. */
1881
+ .p60-col-boxed {
1882
+ border: 1px solid var(--line);
1883
+ border-radius: var(--radius);
1884
+ background: var(--bg-tint);
1885
+ padding: 1rem 1.15rem;
1886
+ }
1887
+ @media (max-width: 720px) {
1888
+ .p60-cols {
1889
+ grid-template-columns: 1fr;
1890
+ gap: 1rem;
1891
+ }
1892
+ }
1893
+
1626
1894
  .service-body {
1627
1895
  color: var(--ink);
1628
1896
  line-height: 1.7;
@@ -2099,14 +2367,14 @@ textarea.p60-form-control {
2099
2367
  @media (max-width: 640px) {
2100
2368
  .article-grid {
2101
2369
  display: flex;
2102
- width: 100vw;
2103
- margin-left: calc(50% - 50vw);
2370
+ inline-size: 100vw;
2371
+ margin-inline-start: calc(50% - 50vw);
2104
2372
  overflow-x: auto;
2105
2373
  -webkit-overflow-scrolling: touch;
2106
2374
  scroll-snap-type: x proximity;
2107
2375
  gap: 0.9rem;
2108
2376
  padding: 0.2rem 22px 0.6rem;
2109
- scroll-padding-left: 22px;
2377
+ scroll-padding-inline-start: 22px;
2110
2378
  scrollbar-width: none;
2111
2379
  }
2112
2380
  .article-grid::-webkit-scrollbar { display: none; }
@@ -2510,8 +2778,8 @@ textarea.p60-form-control {
2510
2778
  @media (max-width: 640px) {
2511
2779
  .events-carousel {
2512
2780
  position: relative;
2513
- width: 100vw;
2514
- margin-left: calc(50% - 50vw);
2781
+ inline-size: 100vw;
2782
+ margin-inline-start: calc(50% - 50vw);
2515
2783
  gap: 0;
2516
2784
  }
2517
2785
  .events-carousel .carousel-slide { border-radius: 0; }
@@ -4164,6 +4432,47 @@ textarea.p60-form-control {
4164
4432
  }
4165
4433
  .member-course-feedback-toggle:hover { opacity: 1; text-decoration: underline; }
4166
4434
  .member-course-feedback-body { margin-top: 0.6rem; display: grid; gap: 0.75rem; }
4435
+ /* The course's structure as a family reads it: parts in order, where the class is, whether their
4436
+ learner was there, and the marks the course lets through. Indentation is the only thing that
4437
+ says which level a part sits at, because the composer named the levels. */
4438
+ .member-course-structure { margin-top: 0.6rem; display: grid; gap: 0.4rem; }
4439
+ .member-course-structure-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.2rem; }
4440
+ .member-course-structure-node { display: flex; justify-content: space-between; gap: 0.75rem; padding: 0.15rem 0; }
4441
+ .member-course-structure-node.is-heading .member-course-structure-name { font-weight: 600; }
4442
+ .member-course-structure-node.is-leaf:not(.is-covered) { opacity: 0.6; }
4443
+ .member-course-structure-node.is-current .member-course-structure-name { font-weight: 600; }
4444
+ .member-course-structure-now { font-style: normal; font-size: 0.8em; margin-inline-start: 0.4rem; padding: 0 0.4rem; border-radius: 999px; background: color-mix(in srgb, currentColor 12%, transparent); }
4445
+ .member-course-structure-state { display: inline-flex; gap: 0.5rem; align-items: baseline; white-space: nowrap; font-size: 0.9em; }
4446
+ .member-course-structure-mark { font-style: normal; font-weight: 600; }
4447
+ .member-course-structure-completion { margin: 0; font-weight: 600; }
4448
+ /* The progress meter: one segment per module, one cell per unit. Fills are the theme's primary so
4449
+ every template recolours it; every state also carries a word, a tick or a hatch, never colour
4450
+ alone. A 2px surface gap separates fills, and the current unit is lit rather than recoloured. */
4451
+ .member-course-meter { display: grid; gap: 0.75rem; padding: 0.9rem 1rem; border: 1px solid var(--line); border-radius: 0.75rem; background: color-mix(in srgb, var(--primary) 6%, transparent); }
4452
+ .member-course-meter-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-end; gap: 0.5rem 1.25rem; }
4453
+ .member-course-meter-hero { display: flex; align-items: baseline; gap: 0.6rem; }
4454
+ .member-course-meter-number { font-size: 2.1rem; font-weight: 800; line-height: 1; letter-spacing: -0.02em; color: var(--primary); }
4455
+ .member-course-meter-caption { font-size: 0.9rem; color: var(--muted); }
4456
+ .member-course-meter-now { display: grid; gap: 0.15rem; font-size: 0.9rem; font-weight: 600; text-align: end; }
4457
+ .member-course-meter-presence { font-weight: 400; color: var(--muted); }
4458
+ .member-course-meter-bar { display: flex; gap: 0.5rem; align-items: flex-start; }
4459
+ .member-course-meter-seg { display: grid; gap: 0.4rem; min-inline-size: 0; }
4460
+ .member-course-meter-cells { display: flex; gap: 2px; }
4461
+ .member-course-meter-cell { flex: 1 1 0; display: inline-block; min-inline-size: 0.6rem; block-size: 0.85rem; border-radius: 4px; background: color-mix(in srgb, currentColor 14%, transparent); }
4462
+ .member-course-meter-cell.is-covered { background: var(--primary); }
4463
+ .member-course-meter-cell.is-covered.is-away { background: repeating-linear-gradient(135deg, var(--primary) 0 3px, color-mix(in srgb, var(--primary) 35%, transparent) 3px 6px); }
4464
+ .member-course-meter-cell.is-current { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--primary); animation: member-course-meter-pulse 2.4s ease-in-out infinite; }
4465
+ @keyframes member-course-meter-pulse { 0%, 100% { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--primary); } 50% { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px color-mix(in srgb, var(--primary) 35%, transparent); } }
4466
+ @media (prefers-reduced-motion: reduce) { .member-course-meter-cell.is-current { animation: none; } }
4467
+ .member-course-meter-seg-label { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.5rem; font-size: 0.8rem; min-inline-size: 0; }
4468
+ .member-course-meter-seg-name { font-weight: 600; overflow-wrap: anywhere; }
4469
+ .member-course-meter-legend { display: flex; flex-wrap: wrap; gap: 0.35rem 1rem; font-size: 0.75rem; color: var(--muted); }
4470
+ .member-course-meter-legend i { display: inline-block; inline-size: 0.9rem; block-size: 0.6rem; vertical-align: middle; margin-inline-end: 0.15rem; }
4471
+ .member-course-meter-legend i.is-current { animation: none; }
4472
+ .member-course-structure-details summary { cursor: pointer; }
4473
+ .member-course-mini { display: block; inline-size: min(14rem, 100%); block-size: 4px; margin-block-start: 0.35rem; border-radius: 999px; background: color-mix(in srgb, currentColor 14%, transparent); overflow: hidden; }
4474
+ .member-course-mini-fill { display: block; block-size: 100%; background: var(--primary); border-radius: inherit; }
4475
+ @media (max-width: 560px) { .member-course-meter-bar { flex-direction: column; gap: 0.6rem; } .member-course-meter-now { text-align: start; } }
4167
4476
  .member-course-feedback-note {
4168
4477
  border-left: 3px solid currentColor;
4169
4478
  padding: 0.35rem 0 0.35rem 0.75rem;
@@ -4721,8 +5030,8 @@ textarea.p60-form-control {
4721
5030
  Cooperative gestures keep page-scrolling safe over the full-width canvas. */
4722
5031
  @media (max-width: 640px) {
4723
5032
  .impact-map-stage {
4724
- width: 100vw;
4725
- margin-left: calc(50% - 50vw);
5033
+ inline-size: 100vw;
5034
+ margin-inline-start: calc(50% - 50vw);
4726
5035
  border-radius: 0;
4727
5036
  }
4728
5037
  }