@port60/template-kit 0.11.0 → 0.12.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@port60/template-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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": {
|
|
@@ -892,6 +892,12 @@
|
|
|
892
892
|
"kind": "text",
|
|
893
893
|
"required": false,
|
|
894
894
|
"description": "Optional introduction."
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
"name": "mapSlug",
|
|
898
|
+
"kind": "text",
|
|
899
|
+
"required": false,
|
|
900
|
+
"description": "WHICH map renders here, by its address (the Pages editor's placement picker writes it; the Maps area is where maps exist). Absent = the tenant's first published map."
|
|
895
901
|
}
|
|
896
902
|
],
|
|
897
903
|
"minimal": {},
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
{ "name": "--accent", "description": "Action accent (the donate CTA family)." },
|
|
13
13
|
{ "name": "--accent-strong", "description": "Darker accent." },
|
|
14
14
|
{ "name": "--gold", "description": "Highlight tint (marks, ratings)." },
|
|
15
|
+
{ "name": "--marker", "description": "Impact-map pin colour (the live-map markers are a zoom-scaling circle layer that take this colour). Defaults to --accent." },
|
|
16
|
+
{ "name": "--map-mode", "description": "Impact-map basemap colour mode read by the map island: auto (derive the map's paper/ink from the theme), light, dark, or muted. Default auto." },
|
|
17
|
+
{ "name": "--map-projection", "description": "Impact-map projection: flat (default) or globe — the planet as a rotatable sphere with atmosphere." },
|
|
15
18
|
{ "name": "--line", "description": "Hairline borders." },
|
|
16
19
|
{ "name": "--line-strong", "description": "Emphasised borders (selected, focus)." },
|
|
17
20
|
{ "name": "--danger", "description": "Errors and destructive affordances." },
|
|
@@ -20,6 +20,15 @@
|
|
|
20
20
|
--accent: #ef6044;
|
|
21
21
|
--accent-strong: #d94d33;
|
|
22
22
|
--gold: #f3b13b;
|
|
23
|
+
/* Impact map. --marker = the map pin colour (a deliberate brand accent by default; the live-map
|
|
24
|
+
markers are a zoom-scaling MapLibre circle layer that takes this colour). --map-mode = the
|
|
25
|
+
basemap colour mode read by the map island: `auto` derives the map's paper/ink from the theme
|
|
26
|
+
neutrals below, `light`/`dark`/`muted` force a base. Template-owned — set in theme.css or exposed
|
|
27
|
+
as Appearance knobs; the tenant never edits map colour directly. */
|
|
28
|
+
--marker: var(--accent);
|
|
29
|
+
--map-mode: auto;
|
|
30
|
+
/* flat (default) or globe — MapLibre's own globe projection, a rotatable sphere. */
|
|
31
|
+
--map-projection: flat;
|
|
23
32
|
--line: rgba(31, 42, 51, 0.1);
|
|
24
33
|
--line-strong: rgba(18, 135, 122, 0.28);
|
|
25
34
|
--danger: #c0392b;
|
|
@@ -953,6 +962,22 @@ textarea.p60-form-control {
|
|
|
953
962
|
gap: 1rem;
|
|
954
963
|
}
|
|
955
964
|
|
|
965
|
+
/* MOBILE BREAKOUT (owner, 2026-08-17): on phones the nesting stack squashes the card badly —
|
|
966
|
+
measured on Sanctuary: 390px viewport → 346 container → 294 aside → 249px card → ~195px of
|
|
967
|
+
actual controls. A donate form is a working control surface; it gets the full screen width
|
|
968
|
+
(edge to edge, side borders/radius dropped, its own padding kept). The negative-margin trick
|
|
969
|
+
escapes ANY wrapper depth; layouts here are symmetric, so the sheet lands exactly 0..100vw. */
|
|
970
|
+
@media (max-width: 640px) {
|
|
971
|
+
.donate-card {
|
|
972
|
+
width: 100vw;
|
|
973
|
+
margin-left: calc(50% - 50vw);
|
|
974
|
+
border-radius: 0;
|
|
975
|
+
border-left: 0;
|
|
976
|
+
border-right: 0;
|
|
977
|
+
padding: 1.4rem 1.1rem;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
956
981
|
/* Optional cause image banner at the top of the widget (set per-cause in tenant-admin). */
|
|
957
982
|
.donate-cause-img {
|
|
958
983
|
width: 100%;
|
|
@@ -1966,6 +1991,30 @@ textarea.p60-form-control {
|
|
|
1966
1991
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
|
1967
1992
|
gap: 1.5rem;
|
|
1968
1993
|
}
|
|
1994
|
+
/* MOBILE: the stacked grid becomes an edge-to-edge SWIPE ROW with a peek (owner, 2026-08-17) —
|
|
1995
|
+
the scroller bleeds to the screen edges, each card keeps its padding, the first card aligns to
|
|
1996
|
+
the text gutter, and the cut-off next card IS the "swipe me" affordance (the reference maps'
|
|
1997
|
+
card idiom). Snap keeps cards settling cleanly; the scrollbar is hidden, the peek replaces it. */
|
|
1998
|
+
@media (max-width: 640px) {
|
|
1999
|
+
.article-grid {
|
|
2000
|
+
display: flex;
|
|
2001
|
+
width: 100vw;
|
|
2002
|
+
margin-left: calc(50% - 50vw);
|
|
2003
|
+
overflow-x: auto;
|
|
2004
|
+
-webkit-overflow-scrolling: touch;
|
|
2005
|
+
scroll-snap-type: x proximity;
|
|
2006
|
+
gap: 0.9rem;
|
|
2007
|
+
padding: 0.2rem 22px 0.6rem;
|
|
2008
|
+
scroll-padding-left: 22px;
|
|
2009
|
+
scrollbar-width: none;
|
|
2010
|
+
}
|
|
2011
|
+
.article-grid::-webkit-scrollbar { display: none; }
|
|
2012
|
+
.article-grid > .article-card {
|
|
2013
|
+
flex: 0 0 78%;
|
|
2014
|
+
max-width: 330px;
|
|
2015
|
+
scroll-snap-align: start;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
1969
2018
|
.article-card {
|
|
1970
2019
|
display: flex;
|
|
1971
2020
|
flex-direction: column;
|
|
@@ -2355,6 +2404,26 @@ textarea.p60-form-control {
|
|
|
2355
2404
|
align-items: center;
|
|
2356
2405
|
gap: 0.5rem;
|
|
2357
2406
|
}
|
|
2407
|
+
/* MOBILE: the rotator is MEDIA — it bleeds edge to edge like the hero and the map, with the
|
|
2408
|
+
arrows overlaid on the image instead of eating width beside it. */
|
|
2409
|
+
@media (max-width: 640px) {
|
|
2410
|
+
.events-carousel {
|
|
2411
|
+
position: relative;
|
|
2412
|
+
width: 100vw;
|
|
2413
|
+
margin-left: calc(50% - 50vw);
|
|
2414
|
+
gap: 0;
|
|
2415
|
+
}
|
|
2416
|
+
.events-carousel .carousel-slide { border-radius: 0; }
|
|
2417
|
+
.events-carousel > .carousel-arrow {
|
|
2418
|
+
position: absolute;
|
|
2419
|
+
top: 50%;
|
|
2420
|
+
transform: translateY(-50%);
|
|
2421
|
+
z-index: 2;
|
|
2422
|
+
opacity: 0.9;
|
|
2423
|
+
}
|
|
2424
|
+
.events-carousel > .carousel-arrow:first-of-type { left: 10px; }
|
|
2425
|
+
.events-carousel > .carousel-arrow:last-of-type { right: 10px; }
|
|
2426
|
+
}
|
|
2358
2427
|
.carousel-slide {
|
|
2359
2428
|
position: relative;
|
|
2360
2429
|
flex: 1;
|
|
@@ -4228,23 +4297,80 @@ textarea.p60-form-control {
|
|
|
4228
4297
|
.people-grid { grid-template-columns: 1fr; }
|
|
4229
4298
|
}
|
|
4230
4299
|
|
|
4231
|
-
/* ── Impact map island (WP4) —
|
|
4232
|
-
|
|
4300
|
+
/* ── Impact map island (WP4) — the OVERLAY borrows the theme: pins wear --marker (a brand accent by
|
|
4301
|
+
default), the card is a themed --surface. The BASEMAP colour is derived by the map island from the
|
|
4302
|
+
theme + --map-mode (see lib/mapTheme.ts). Brand on the overlay, neutrals on the map — the recipe
|
|
4303
|
+
that stays legible on any theme. Templates restyle any of this via the tokens or these classes. */
|
|
4233
4304
|
.impact-map { margin: 24px 0; }
|
|
4234
4305
|
.impact-map-head h2 { margin: 0 0 6px; }
|
|
4235
4306
|
.impact-map-intro { margin: 0 0 14px; opacity: 0.85; }
|
|
4236
|
-
.impact-map-
|
|
4237
|
-
|
|
4307
|
+
.impact-map-stats { margin: 0 0 14px; color: var(--muted); font-weight: 600; letter-spacing: 0.01em; }
|
|
4308
|
+
/* The stable stage, shaped like THE WORLD BAND ITSELF: the island's planet view is all 360° of
|
|
4309
|
+
longitude by 84°N–58°S (whole Greenland in, only the polar void and Antarctica trimmed), which in
|
|
4310
|
+
Web Mercator is a 1.497:1 box — so a frame with that exact ratio holds the full planet edge to
|
|
4311
|
+
edge, full width AND full height, at any screen size. A ratio wider than the band (a max-height
|
|
4312
|
+
clamp once made it so on large screens) turns the fit height-constrained and the world renders
|
|
4313
|
+
NARROWER than the frame — dead bars at the sides; a narrower ratio (min-height on phones) just
|
|
4314
|
+
adds benign vertical margin. Relative + clipped so MapLibre always has a real box and can never
|
|
4315
|
+
escape it. Keep this ratio in step with the island's planet bounds. */
|
|
4316
|
+
.impact-map-stage { position: relative; width: 100%; aspect-ratio: 1.497; min-height: 300px;
|
|
4317
|
+
border-radius: 14px; overflow: hidden; }
|
|
4318
|
+
/* width:100% is LOAD-BEARING: with height pinned by min-height and width left auto, CSS derives
|
|
4319
|
+
width FROM the aspect-ratio (300 x 1.497 = 449px) — on phones the stage overflowed the page
|
|
4320
|
+
sideways, shoving every other section into a lopsided "left with a right gutter" layout. Width
|
|
4321
|
+
pinned, the ratio yields height and min-height only ever makes the stage TALLER. */
|
|
4322
|
+
/* MOBILE BREAKOUT: a map is a canvas — every pixel is information, nothing needs a margin — so on
|
|
4323
|
+
phones it bleeds edge to edge (media bleeds, prose gutters: the hero already sets the rhythm).
|
|
4324
|
+
Cooperative gestures keep page-scrolling safe over the full-width canvas. */
|
|
4325
|
+
@media (max-width: 640px) {
|
|
4326
|
+
.impact-map-stage {
|
|
4327
|
+
width: 100vw;
|
|
4328
|
+
margin-left: calc(50% - 50vw);
|
|
4329
|
+
border-radius: 0;
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
.impact-map-canvas { position: absolute; inset: 0;
|
|
4333
|
+
background: linear-gradient(160deg, rgba(0, 0, 0, 0.28), rgba(0, 0, 0, 0.55)), var(--muted); }
|
|
4238
4334
|
.impact-map-pin { position: absolute; width: 16px; height: 16px; border-radius: 50%;
|
|
4239
|
-
transform: translate(-50%, -50%); border: 2px solid
|
|
4335
|
+
transform: translate(-50%, -50%); border: 2px solid var(--surface); background: var(--marker);
|
|
4240
4336
|
cursor: pointer; padding: 0; transition: transform 120ms ease; }
|
|
4241
4337
|
.impact-map-pin:hover, .impact-map-pin:focus-visible { transform: translate(-50%, -50%) scale(1.35); }
|
|
4242
|
-
.impact-map-pin--active { box-shadow: 0 0 0 5px
|
|
4243
|
-
.impact-map-card { margin-top: 14px;
|
|
4338
|
+
.impact-map-pin--active { box-shadow: 0 0 0 5px var(--primary-soft); }
|
|
4339
|
+
.impact-map-card { margin-top: 14px; background: var(--surface); color: var(--ink);
|
|
4340
|
+
border: 1px solid var(--line); border-radius: var(--radius-sm); box-shadow: var(--shadow-sm);
|
|
4244
4341
|
padding: 16px 18px; max-width: 34rem; }
|
|
4245
4342
|
.impact-map-card-kind { display: inline-block; font-size: 11px; font-weight: 700;
|
|
4246
|
-
letter-spacing: 0.06em; text-transform: uppercase;
|
|
4343
|
+
letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin-bottom: 4px; }
|
|
4247
4344
|
.impact-map-card-title { margin: 0 0 6px; }
|
|
4248
4345
|
.impact-map-card-summary { margin: 0 0 10px; opacity: 0.9; }
|
|
4249
|
-
.impact-map-card-cta { font-weight: 600; }
|
|
4346
|
+
.impact-map-card-cta { font-weight: 600; color: var(--primary); }
|
|
4250
4347
|
.impact-map-empty { opacity: 0.7; }
|
|
4348
|
+
/* The live tile layer (slice 2): fills the stage from first render so MapLibre measures a real box;
|
|
4349
|
+
the pin canvas overlays it until (unless) the map is ready, then unmounts to reveal the map.
|
|
4350
|
+
Sized with width/height (NOT inset) because MapLibre forces position:relative on this element,
|
|
4351
|
+
which would defeat absolute insets and collapse the box to zero height. */
|
|
4352
|
+
.impact-map-gl { width: 100%; height: 100%; }
|
|
4353
|
+
/* Live-map markers are a MapLibre circle layer (WebGL) — they scale with zoom and take their colour
|
|
4354
|
+
from --marker (circle-color) — so there is no DOM marker element to style here. The pin canvas
|
|
4355
|
+
fallback above keeps its own .impact-map-pin dots for the no-tiles case. */
|
|
4356
|
+
/* The INLINE marker card (a MapLibre popup anchored at the clicked marker — where "See more"
|
|
4357
|
+
lives): the shell takes the theme's surface; the tip is recoloured per anchor side to match. */
|
|
4358
|
+
.impact-map-popup .maplibregl-popup-content { background: var(--surface); color: var(--ink);
|
|
4359
|
+
border: 1px solid var(--line); border-radius: var(--radius-sm); box-shadow: var(--shadow);
|
|
4360
|
+
padding: 14px 16px; }
|
|
4361
|
+
.impact-map-popup.maplibregl-popup-anchor-bottom .maplibregl-popup-tip,
|
|
4362
|
+
.impact-map-popup.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip,
|
|
4363
|
+
.impact-map-popup.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip { border-top-color: var(--surface); }
|
|
4364
|
+
.impact-map-popup.maplibregl-popup-anchor-top .maplibregl-popup-tip,
|
|
4365
|
+
.impact-map-popup.maplibregl-popup-anchor-top-left .maplibregl-popup-tip,
|
|
4366
|
+
.impact-map-popup.maplibregl-popup-anchor-top-right .maplibregl-popup-tip { border-bottom-color: var(--surface); }
|
|
4367
|
+
.impact-map-popup.maplibregl-popup-anchor-left .maplibregl-popup-tip { border-right-color: var(--surface); }
|
|
4368
|
+
.impact-map-popup.maplibregl-popup-anchor-right .maplibregl-popup-tip { border-left-color: var(--surface); }
|
|
4369
|
+
.impact-map-popup-card .impact-map-card-title { margin: 4px 0 6px; font-size: 1.02rem; }
|
|
4370
|
+
.impact-map-popup-card .impact-map-card-summary { margin: 0 0 8px; }
|
|
4371
|
+
/* The point's image (when it has one): bleeds to the popup's top edge; plain rounded in the
|
|
4372
|
+
fallback card. Absent image = the card simply starts at the kind line. */
|
|
4373
|
+
.impact-map-card-image { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
|
|
4374
|
+
border-radius: 10px; margin: 0 0 10px; }
|
|
4375
|
+
.impact-map-popup-card .impact-map-card-image { width: calc(100% + 32px); margin: -14px -16px 10px;
|
|
4376
|
+
border-radius: calc(var(--radius-sm) - 1px) calc(var(--radius-sm) - 1px) 0 0; }
|
package/starter/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"format": "port60-liquid@1",
|
|
5
5
|
"label": "Starter",
|
|
6
6
|
"description": "The reference template for the Port60 dialect, the developer docs' worked example and the base to copy when building your own. 1.1.0 adds the layout slot: the template owns the header, navigation and footer chrome.",
|
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
"values",
|
|
16
16
|
"cta",
|
|
17
17
|
"people",
|
|
18
|
-
"campaigns"
|
|
18
|
+
"campaigns",
|
|
19
|
+
"impactMap"
|
|
19
20
|
],
|
|
20
21
|
"islands": [
|
|
21
22
|
"donation_widget",
|
|
22
23
|
"member_menu",
|
|
23
|
-
"hero_carousel"
|
|
24
|
+
"hero_carousel",
|
|
25
|
+
"map"
|
|
24
26
|
],
|
|
25
27
|
"layout": true,
|
|
26
28
|
"heroImagery": true,
|
|
@@ -46,7 +48,7 @@
|
|
|
46
48
|
}
|
|
47
49
|
]
|
|
48
50
|
},
|
|
49
|
-
"changelog": "
|
|
51
|
+
"changelog": "Adds the impact map section: pin your located work on a themed map of where you work.",
|
|
50
52
|
"imagery": {
|
|
51
53
|
"hero": {
|
|
52
54
|
"idealAspect": "16:9",
|