pandorga 1.10.1 → 1.11.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -0
- data/DESIGN.md +7 -5
- data/_includes/content-runtime/43-blocks.html +6 -36
- data/_includes/home/band-listing.html +3 -5
- data/_layouts/home.html +6 -18
- data/assets/css/ledger/home.css +61 -95
- data/lib/pandorga/commands/new.rb +1 -1
- data/lib/pandorga/version.rb +1 -1
- data/scripts/test/render-parity/blocks/project-inventory-row.json +8 -11
- data/scripts/test/test-css-structure.rb +57 -0
- data/scripts/test/test-home-band-intro.rb +97 -0
- data/scripts/validate.sh +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d3adc84d250c4ff140df5b9cc9db266896fdfce92fd899789fd943ce8ac4659
|
|
4
|
+
data.tar.gz: 474d788dea7851796fb189732c9bb75d118666177f4cf81df70d8361f4310872
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d9e76b0be8485c3870366cce0bc816fc55f3dee7ea2b17ec40602f2c70dccbf985e2d7be6da7911e54a7f830c6fd3c4030dbb985689d50c84cb9226c04cd0f2e
|
|
7
|
+
data.tar.gz: 6853fe1a1d0edd4066d45187a4cddd67258a753afd849284615383fb9cae4844a24aabff188681a73066e2808170bfc1e28730832e68d976c34400899d2cea88
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [1.11.0] — 2026-10-19
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Home listing bands keep their kicker and note on sites built with
|
|
10
|
+
`shell_content_agnostic: true`; the slots are always in the HTML and the
|
|
11
|
+
runtime fills them from `pages/headers.json` (regression from 1.10.0)
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- On the home projects bench at or above 900px, the specimen column takes 35%
|
|
16
|
+
of the band (at least 20rem) and the filing rack 45% (at least 21rem) across
|
|
17
|
+
one `--space-xl` column gap from the band's left edge, keeping the 16:9
|
|
18
|
+
plate at 60% of its previous height. The specimen sets the bench height (the
|
|
19
|
+
tallest project), and the rack rows share that height, each capped at 1.6
|
|
20
|
+
times its minimum
|
|
21
|
+
- Below 900px the project icon-strip glyphs and the plate badge glyph grow to
|
|
22
|
+
1.375rem
|
|
23
|
+
- The home project specimen title uses `--type-section` from 900px and
|
|
24
|
+
`--type-card-lg` below it; inventory rack titles use `--type-card`
|
|
25
|
+
- The home specimen meta strip wraps instead of clipping on narrow viewports
|
|
26
|
+
- The home hero summary uses `--color-legend`, the same ink as the other
|
|
27
|
+
description texts
|
|
28
|
+
|
|
29
|
+
## [1.10.2] — 2026-10-16
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- On the home projects bench, each inventory row shows the project icon mark
|
|
34
|
+
(or initial fallback), optional index and label strip, and title, without a
|
|
35
|
+
thumbnail poster, tagline preview, or attached-resource icons. On desktop
|
|
36
|
+
(`>= 900px`) each row fits that two-line body and square icon mark, and the
|
|
37
|
+
specimen height matches the inventory stack
|
|
38
|
+
- Project hero action pills keep their label text free of underlines at rest
|
|
39
|
+
and on hover
|
|
40
|
+
|
|
3
41
|
## [1.10.1] — 2026-10-14
|
|
4
42
|
|
|
5
43
|
### Changed
|
data/DESIGN.md
CHANGED
|
@@ -941,13 +941,15 @@ Letter-spacing in use, by frequency: `0.1em` (12), `0.08em` (10), `0.12em` (7),
|
|
|
941
941
|
icon plate, the name's initial standing in for a missing icon; the title
|
|
942
942
|
always below the plate; a tagline clamped to the
|
|
943
943
|
height the plate leaves; tags, outbound links and an emphasis "Open
|
|
944
|
-
project" cue). From 900px
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
944
|
+
project" cue). From 900px the specimen and a `panelContrast` filing rack
|
|
945
|
+
take 35% and 45% of the band across a `--space-xl` gap, from its left edge;
|
|
946
|
+
rack rows are equal-flex
|
|
947
|
+
cards (square icon mark, code line, title; the selected one steps to
|
|
948
|
+
`surfaceHigh` with a 3px `primary` inset rule) that share the specimen's
|
|
949
|
+
height. Rack click swaps the specimen
|
|
948
950
|
in place; specimen click opens the project. Below 900px the rack is an
|
|
949
951
|
icon strip above the specimen, one equal column per project (up to six)
|
|
950
|
-
so it spans the band. Contract: `PLT-AC-13`–`17` in
|
|
952
|
+
so it spans the band, with 1.375rem glyphs. Contract: `PLT-AC-13`–`17` in
|
|
951
953
|
`templates/portfolio/README.md`. Below 768px the hero portrait runs to the screen edge, pulled
|
|
952
954
|
out of the band's gutter by exactly `--gutter-mobile` on each side and losing
|
|
953
955
|
its side hairlines, which at the edge would read as a frame cut off.
|
|
@@ -330,9 +330,9 @@
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
/* Rack card for the home portfolio bench.
|
|
333
|
-
* Desktop:
|
|
334
|
-
*
|
|
335
|
-
*
|
|
333
|
+
* Desktop: square icon mark (or initial) beside a code line and title.
|
|
334
|
+
* The mobile strip shows the project glyph only. Selectable rows update
|
|
335
|
+
* the specimen. */
|
|
336
336
|
function renderProjectInventoryRow(item, options) {
|
|
337
337
|
options = options || {};
|
|
338
338
|
var f = blockFields(item);
|
|
@@ -357,12 +357,12 @@
|
|
|
357
357
|
row.setAttribute('aria-label', name);
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
var media = renderPosterMedia(fm.thumbnail, name);
|
|
361
360
|
var initial = String(name || '?').charAt(0).toUpperCase();
|
|
362
|
-
|
|
361
|
+
var media;
|
|
362
|
+
if (fm.icon) {
|
|
363
363
|
media = '<div class="home-project-inventory-mark" aria-hidden="true">' +
|
|
364
364
|
'<span class="material-symbols-outlined">' + escapeHtml(fm.icon) + '</span></div>';
|
|
365
|
-
} else
|
|
365
|
+
} else {
|
|
366
366
|
media = '<div class="home-project-inventory-mark home-project-inventory-mark--initial" aria-hidden="true">' +
|
|
367
367
|
escapeHtml(initial) + '</div>';
|
|
368
368
|
}
|
|
@@ -388,9 +388,6 @@
|
|
|
388
388
|
'<a class="ledger-card-stretch" href="' + escapeAttr(href) + '">' +
|
|
389
389
|
escapeHtml(name) + '</a></h3>';
|
|
390
390
|
}
|
|
391
|
-
if (fm.tagline) {
|
|
392
|
-
html += '<p class="home-project-inventory-copy font-body">' + escapeHtml(fm.tagline) + '</p>';
|
|
393
|
-
}
|
|
394
391
|
html += '</div>';
|
|
395
392
|
if (fm.icon) {
|
|
396
393
|
html += '<span class="home-project-inventory-glyph material-symbols-outlined" aria-hidden="true">' +
|
|
@@ -399,7 +396,6 @@
|
|
|
399
396
|
html += '<span class="home-project-inventory-glyph home-project-inventory-glyph--initial" aria-hidden="true">' +
|
|
400
397
|
escapeHtml(initial) + '</span>';
|
|
401
398
|
}
|
|
402
|
-
html += renderProjectResourceBadges(fm);
|
|
403
399
|
|
|
404
400
|
row.innerHTML = html;
|
|
405
401
|
return row;
|
|
@@ -473,32 +469,6 @@
|
|
|
473
469
|
return links.join('');
|
|
474
470
|
}
|
|
475
471
|
|
|
476
|
-
/* Square resource marks for the home inventory rack — same fields as
|
|
477
|
-
* renderProjectLinks (github / article_url / external_url), icon-only
|
|
478
|
-
* indicators (not links). */
|
|
479
|
-
function renderProjectResourceBadges(fm) {
|
|
480
|
-
var badges = [];
|
|
481
|
-
if (fm.github) {
|
|
482
|
-
badges.push({ icon: 'code', label: 'Code' });
|
|
483
|
-
}
|
|
484
|
-
if (fm.article_url) {
|
|
485
|
-
badges.push({ icon: 'article', label: 'Article' });
|
|
486
|
-
}
|
|
487
|
-
if (fm.external_url) {
|
|
488
|
-
badges.push({ icon: 'public', label: 'Site' });
|
|
489
|
-
}
|
|
490
|
-
if (!badges.length) return '';
|
|
491
|
-
return '<div class="home-project-inventory-resources" aria-label="Attached resources">' +
|
|
492
|
-
badges.map(function (badge) {
|
|
493
|
-
return '<span class="home-project-inventory-resource" title="' +
|
|
494
|
-
escapeAttr(badge.label) + '" aria-label="' +
|
|
495
|
-
escapeAttr(badge.label) + '">' +
|
|
496
|
-
'<span class="material-symbols-outlined" aria-hidden="true">' +
|
|
497
|
-
escapeHtml(badge.icon) + '</span></span>';
|
|
498
|
-
}).join('') +
|
|
499
|
-
'</div>';
|
|
500
|
-
}
|
|
501
|
-
|
|
502
472
|
/* Home specimen — the featured project on the portfolio bench. A system
|
|
503
473
|
* card whose strip carries folio, label and start year, with the title
|
|
504
474
|
* below a 16:9 plate: the thumbnail (icon badge on it) or else the icon
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{%- comment -%}
|
|
2
2
|
One homepage listing band. Expects `band` from site.data.pandorga_home_bands.
|
|
3
|
-
|
|
3
|
+
Kicker and note slots are always emitted, prefilled from headers.yml when it
|
|
4
|
+
was read at build time and filled by the runtime otherwise (PLT-AC-42).
|
|
5
|
+
Listing slots are filled by the home layout script from writing_index.json.
|
|
4
6
|
{%- endcomment -%}
|
|
5
7
|
{%- assign _accent = band.accent -%}
|
|
6
8
|
{%- assign _limit = band.home.limit | default: 3 -%}
|
|
@@ -28,13 +30,9 @@
|
|
|
28
30
|
{%- if _section.title != blank -%}{%- assign _title = _section.title -%}{%- endif -%}
|
|
29
31
|
<div class="ledger-section-banner" data-content-home-section="{{ band.section_key }}">
|
|
30
32
|
<div class="ledger-section-lead">
|
|
31
|
-
{%- if _section.kicker != blank -%}
|
|
32
33
|
<span class="ledger-kicker" data-content-section-kicker>{{ _section.kicker }}</span>
|
|
33
|
-
{%- endif -%}
|
|
34
34
|
<h2 class="section-title" data-content-section-title>{{ _title }}</h2>
|
|
35
|
-
{%- if _section.note != blank -%}
|
|
36
35
|
<p class="home-section-note font-body" data-content-section-note>{{ _section.note }}</p>
|
|
37
|
-
{%- endif -%}
|
|
38
36
|
</div>
|
|
39
37
|
<div class="home-section-aside">
|
|
40
38
|
<a class="ledger-cta" href="{{ _path }}">
|
data/_layouts/home.html
CHANGED
|
@@ -399,22 +399,12 @@ layout: shell
|
|
|
399
399
|
async function lockSpecimenHeight() {
|
|
400
400
|
var bench = specimenSlot.closest('.home-projects-bench');
|
|
401
401
|
if (!bench) return;
|
|
402
|
+
bench.style.setProperty('--home-project-icon-count', String(iconSlots));
|
|
402
403
|
var tallest = await measureTallestSpecimen();
|
|
403
|
-
/*
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
.getPropertyValue('--home-inventory-min-height').trim();
|
|
408
|
-
if (raw) {
|
|
409
|
-
var probe = document.createElement('div');
|
|
410
|
-
probe.style.cssText = 'position:absolute;visibility:hidden;height:' + raw;
|
|
411
|
-
bench.appendChild(probe);
|
|
412
|
-
inventoryMin = Math.ceil(probe.getBoundingClientRect().height);
|
|
413
|
-
probe.remove();
|
|
414
|
-
}
|
|
415
|
-
var locked = Math.max(tallest, inventoryMin);
|
|
416
|
-
if (locked > 0) {
|
|
417
|
-
bench.style.setProperty('--home-specimen-height', locked + 'px');
|
|
404
|
+
/* Every breakpoint locks the specimen to the tallest project; from
|
|
405
|
+
900px the rack rows share that height. */
|
|
406
|
+
if (tallest > 0) {
|
|
407
|
+
bench.style.setProperty('--home-specimen-height', tallest + 'px');
|
|
418
408
|
}
|
|
419
409
|
}
|
|
420
410
|
|
|
@@ -441,12 +431,10 @@ layout: shell
|
|
|
441
431
|
selected: entry.slug === selectedSlug,
|
|
442
432
|
index: i + 1
|
|
443
433
|
});
|
|
444
|
-
row.addEventListener('click', function (
|
|
445
|
-
if (event.target.closest('.home-project-inventory-resources')) return;
|
|
434
|
+
row.addEventListener('click', function () {
|
|
446
435
|
select(entry.slug);
|
|
447
436
|
});
|
|
448
437
|
row.addEventListener('keydown', function (event) {
|
|
449
|
-
if (event.target.closest('.home-project-inventory-resources')) return;
|
|
450
438
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
451
439
|
event.preventDefault();
|
|
452
440
|
select(entry.slug);
|
data/assets/css/ledger/home.css
CHANGED
|
@@ -308,7 +308,7 @@
|
|
|
308
308
|
|
|
309
309
|
.home-hero-summary {
|
|
310
310
|
max-width: var(--reading-measure, 45rem);
|
|
311
|
-
color: var(--color-
|
|
311
|
+
color: var(--color-legend);
|
|
312
312
|
line-height: var(--leading-body);
|
|
313
313
|
}
|
|
314
314
|
|
|
@@ -1035,10 +1035,11 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
1035
1035
|
the inventory. Taglines clamp to two lines with an ellipsis; chips wrap
|
|
1036
1036
|
whole pills like the projects index; spare height parks chips and the foot
|
|
1037
1037
|
on the floor. Every plate is 16:9, the ratio the thumbnails are drawn at,
|
|
1038
|
-
with the title below it. From 900px the specimen
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1038
|
+
with the title below it. From 900px the specimen column takes 35% of the
|
|
1039
|
+
band (at least 20rem) and the rack 45% (at least 21rem) across one
|
|
1040
|
+
`--space-xl` column gap, starting at the band's left edge, and the rack
|
|
1041
|
+
rows share the specimen height. Below 900px the rack is an icon strip
|
|
1042
|
+
above the specimen, one column per project. */
|
|
1042
1043
|
.home-projects-bench {
|
|
1043
1044
|
--home-specimen-height: 32rem;
|
|
1044
1045
|
--home-icon-btn-size: 2.75rem;
|
|
@@ -1101,10 +1102,18 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
1101
1102
|
letter-spacing: 0.03em;
|
|
1102
1103
|
}
|
|
1103
1104
|
|
|
1105
|
+
/* The specimen strip flows as text, so a long label wraps between words
|
|
1106
|
+
instead of clipping the start year on a phone. */
|
|
1104
1107
|
.home-specimen-strip .ledger-meta {
|
|
1108
|
+
display: block;
|
|
1109
|
+
white-space: normal;
|
|
1105
1110
|
font-size: var(--type-label-lg);
|
|
1106
1111
|
}
|
|
1107
1112
|
|
|
1113
|
+
.home-specimen-strip .ledger-meta-sep {
|
|
1114
|
+
margin-inline: var(--space-sm);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1108
1117
|
.home-specimen-folio,
|
|
1109
1118
|
.home-project-inventory-index {
|
|
1110
1119
|
color: var(--color-primary);
|
|
@@ -1205,6 +1214,22 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
1205
1214
|
color: var(--color-primary);
|
|
1206
1215
|
}
|
|
1207
1216
|
|
|
1217
|
+
@media (max-width: 899px) {
|
|
1218
|
+
.home-specimen-figure:has(> .ledger-poster)>.home-specimen-badge {
|
|
1219
|
+
font-size: 1.375rem;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/* Two classes: the Material Symbols sheet loads after this one and resets
|
|
1223
|
+
the glyph size on a single class. */
|
|
1224
|
+
.home-project-inventory-row .home-project-inventory-glyph {
|
|
1225
|
+
font-size: 1.375rem;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
.home-project-inventory-row .home-project-inventory-glyph--initial {
|
|
1229
|
+
font-size: var(--type-card);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1208
1233
|
.home-project-specimen .home-specimen-title {
|
|
1209
1234
|
margin: 0;
|
|
1210
1235
|
font-size: var(--type-card-lg);
|
|
@@ -1321,10 +1346,6 @@ button.home-project-inventory-row {
|
|
|
1321
1346
|
display: none;
|
|
1322
1347
|
}
|
|
1323
1348
|
|
|
1324
|
-
.home-project-inventory-resources {
|
|
1325
|
-
display: none;
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
1349
|
.home-project-inventory-glyph {
|
|
1329
1350
|
display: inline-flex;
|
|
1330
1351
|
align-items: center;
|
|
@@ -1367,26 +1388,22 @@ button.home-project-inventory-row {
|
|
|
1367
1388
|
|
|
1368
1389
|
@media (min-width: 900px) {
|
|
1369
1390
|
|
|
1370
|
-
/*
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
height
|
|
1391
|
+
/* The specimen column is 35% of the band (at least 20rem), which keeps the
|
|
1392
|
+
16:9 plate at 60% of the 7:5 split's height; the rack is 45% (at least
|
|
1393
|
+
21rem); the pair starts at the band's left edge and leaves the rest of
|
|
1394
|
+
the row blank. The specimen sets the bench height; rack rows share it. */
|
|
1374
1395
|
.home-projects-bench {
|
|
1375
|
-
--home-inventory-
|
|
1376
|
-
--home-inventory-icon-gap: 0.2rem;
|
|
1377
|
-
--home-inventory-resources-height: calc(
|
|
1378
|
-
3 * var(--home-inventory-icon-size) + 2 * var(--home-inventory-icon-gap)
|
|
1379
|
-
);
|
|
1396
|
+
--home-inventory-mark-size: 3rem;
|
|
1380
1397
|
--home-inventory-row-min: calc(
|
|
1381
|
-
var(--home-inventory-
|
|
1382
|
-
);
|
|
1383
|
-
--home-inventory-min-height: calc(
|
|
1384
|
-
6 * var(--home-inventory-row-min) + 5 * var(--space-sm) + 2 * var(--space-md)
|
|
1398
|
+
var(--home-inventory-mark-size) + 2 * var(--space-sm) + 2px
|
|
1385
1399
|
);
|
|
1386
|
-
|
|
1400
|
+
/* Rows share the specimen height, but a short rack must not stretch a
|
|
1401
|
+
single row to the full plate. */
|
|
1402
|
+
--home-inventory-row-max: calc(1.6 * var(--home-inventory-row-min));
|
|
1387
1403
|
display: grid;
|
|
1388
|
-
grid-template-columns:
|
|
1404
|
+
grid-template-columns: max(35%, 20rem) max(45%, 21rem);
|
|
1389
1405
|
column-gap: var(--space-xl);
|
|
1406
|
+
justify-content: start;
|
|
1390
1407
|
align-items: stretch;
|
|
1391
1408
|
}
|
|
1392
1409
|
|
|
@@ -1397,6 +1414,7 @@ button.home-project-inventory-row {
|
|
|
1397
1414
|
gap: var(--space-sm);
|
|
1398
1415
|
box-sizing: border-box;
|
|
1399
1416
|
width: 100%;
|
|
1417
|
+
min-height: var(--home-specimen-height);
|
|
1400
1418
|
min-width: 0;
|
|
1401
1419
|
min-height: 0;
|
|
1402
1420
|
overflow: hidden;
|
|
@@ -1405,16 +1423,18 @@ button.home-project-inventory-row {
|
|
|
1405
1423
|
}
|
|
1406
1424
|
|
|
1407
1425
|
.home-project-specimen .home-specimen-title {
|
|
1408
|
-
font-size: var(--type-
|
|
1426
|
+
font-size: var(--type-section);
|
|
1409
1427
|
}
|
|
1410
1428
|
|
|
1411
1429
|
.home-project-inventory-row {
|
|
1412
1430
|
flex: 1 1 0;
|
|
1413
|
-
align-items:
|
|
1431
|
+
align-items: center;
|
|
1414
1432
|
justify-content: flex-start;
|
|
1415
1433
|
gap: var(--space-md);
|
|
1416
1434
|
height: auto;
|
|
1417
1435
|
min-height: var(--home-inventory-row-min);
|
|
1436
|
+
max-height: var(--home-inventory-row-max);
|
|
1437
|
+
box-sizing: border-box;
|
|
1418
1438
|
padding: var(--space-sm) var(--space-md);
|
|
1419
1439
|
border: 1px solid var(--border-gentle-divider);
|
|
1420
1440
|
background: var(--color-surface-card);
|
|
@@ -1433,15 +1453,13 @@ button.home-project-inventory-row {
|
|
|
1433
1453
|
box-shadow: inset 3px 0 0 var(--color-primary);
|
|
1434
1454
|
}
|
|
1435
1455
|
|
|
1436
|
-
/*
|
|
1437
|
-
it tall while title/copy/icons stay top-aligned with its top edge. */
|
|
1456
|
+
/* Square icon mark beside the two-line code + title body. */
|
|
1438
1457
|
.home-project-inventory-media {
|
|
1439
1458
|
display: block;
|
|
1440
1459
|
flex: 0 0 auto;
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
aspect-ratio: 16 / 9;
|
|
1460
|
+
width: var(--home-inventory-mark-size);
|
|
1461
|
+
height: var(--home-inventory-mark-size);
|
|
1462
|
+
aspect-ratio: 1 / 1;
|
|
1445
1463
|
overflow: hidden;
|
|
1446
1464
|
background: var(--color-surface-high);
|
|
1447
1465
|
}
|
|
@@ -1451,33 +1469,25 @@ button.home-project-inventory-row {
|
|
|
1451
1469
|
outline-offset: 2px;
|
|
1452
1470
|
}
|
|
1453
1471
|
|
|
1454
|
-
.home-project-inventory-media .ledger-poster,
|
|
1455
1472
|
.home-project-inventory-mark {
|
|
1473
|
+
display: flex;
|
|
1474
|
+
align-items: center;
|
|
1475
|
+
justify-content: center;
|
|
1456
1476
|
width: 100%;
|
|
1457
1477
|
height: 100%;
|
|
1458
1478
|
aspect-ratio: auto;
|
|
1459
1479
|
overflow: hidden;
|
|
1460
1480
|
background: var(--color-surface-high);
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
.home-project-inventory-media .ledger-poster::after {
|
|
1464
|
-
display: none;
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
.home-project-inventory-mark {
|
|
1468
|
-
display: flex;
|
|
1469
|
-
align-items: center;
|
|
1470
|
-
justify-content: center;
|
|
1471
1481
|
color: var(--color-primary);
|
|
1472
1482
|
}
|
|
1473
1483
|
|
|
1474
1484
|
.home-project-inventory-mark .material-symbols-outlined {
|
|
1475
|
-
font-size:
|
|
1485
|
+
font-size: 1.5rem;
|
|
1476
1486
|
}
|
|
1477
1487
|
|
|
1478
1488
|
.home-project-inventory-mark--initial {
|
|
1479
1489
|
font-family: var(--font-content-title);
|
|
1480
|
-
font-size: var(--type-card
|
|
1490
|
+
font-size: var(--type-card);
|
|
1481
1491
|
font-weight: var(--weight-title);
|
|
1482
1492
|
line-height: 1;
|
|
1483
1493
|
color: var(--color-on-surface);
|
|
@@ -1486,55 +1496,22 @@ button.home-project-inventory-row {
|
|
|
1486
1496
|
.home-project-inventory-body {
|
|
1487
1497
|
display: flex;
|
|
1488
1498
|
flex-direction: column;
|
|
1489
|
-
justify-content:
|
|
1490
|
-
align-self:
|
|
1491
|
-
gap: 0.
|
|
1499
|
+
justify-content: center;
|
|
1500
|
+
align-self: center;
|
|
1501
|
+
gap: 0.15rem;
|
|
1492
1502
|
flex: 1 1 0;
|
|
1493
1503
|
min-width: 0;
|
|
1494
1504
|
padding-top: 0;
|
|
1495
1505
|
}
|
|
1496
1506
|
|
|
1497
|
-
.home-project-inventory-resources {
|
|
1498
|
-
display: flex;
|
|
1499
|
-
flex-direction: column;
|
|
1500
|
-
justify-content: flex-start;
|
|
1501
|
-
align-items: center;
|
|
1502
|
-
align-self: flex-start;
|
|
1503
|
-
flex: 0 0 auto;
|
|
1504
|
-
gap: var(--home-inventory-icon-gap);
|
|
1505
|
-
width: var(--home-inventory-icon-size);
|
|
1506
|
-
min-height: var(--home-inventory-resources-height);
|
|
1507
|
-
position: relative;
|
|
1508
|
-
z-index: 1;
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
/* Neutral marks — same voice as list descriptions, not primary CTAs. */
|
|
1512
|
-
.home-project-inventory-resource {
|
|
1513
|
-
display: inline-flex;
|
|
1514
|
-
align-items: center;
|
|
1515
|
-
justify-content: center;
|
|
1516
|
-
width: var(--home-inventory-icon-size);
|
|
1517
|
-
height: var(--home-inventory-icon-size);
|
|
1518
|
-
flex: 0 0 auto;
|
|
1519
|
-
background: transparent;
|
|
1520
|
-
color: var(--color-on-surface-variant);
|
|
1521
|
-
border: 0;
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
.home-project-inventory-resource .material-symbols-outlined {
|
|
1525
|
-
font-size: 1rem;
|
|
1526
|
-
line-height: 1;
|
|
1527
|
-
font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 20;
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
1507
|
.home-project-inventory-code .ledger-meta {
|
|
1531
1508
|
font-size: var(--type-label-lg);
|
|
1532
1509
|
}
|
|
1533
1510
|
|
|
1534
1511
|
.home-project-inventory-title {
|
|
1535
|
-
margin: 0
|
|
1512
|
+
margin: 0;
|
|
1536
1513
|
font-family: var(--font-content-title);
|
|
1537
|
-
font-size:
|
|
1514
|
+
font-size: var(--type-card);
|
|
1538
1515
|
font-weight: var(--weight-title);
|
|
1539
1516
|
line-height: 1.2;
|
|
1540
1517
|
color: var(--color-on-surface);
|
|
@@ -1549,17 +1526,6 @@ button.home-project-inventory-row {
|
|
|
1549
1526
|
color: var(--color-primary);
|
|
1550
1527
|
}
|
|
1551
1528
|
|
|
1552
|
-
.home-project-inventory-copy {
|
|
1553
|
-
margin: 0;
|
|
1554
|
-
font-size: var(--type-label-lg);
|
|
1555
|
-
font-weight: var(--weight-body);
|
|
1556
|
-
line-height: 1.35;
|
|
1557
|
-
color: var(--color-on-surface-variant);
|
|
1558
|
-
overflow: hidden;
|
|
1559
|
-
text-overflow: ellipsis;
|
|
1560
|
-
white-space: nowrap;
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
1529
|
/* Two classes: the glyph also carries .material-symbols-outlined, whose
|
|
1564
1530
|
font stylesheet loads after this one and sets display. */
|
|
1565
1531
|
.home-project-inventory-row .home-project-inventory-glyph {
|
|
@@ -1901,7 +1867,7 @@ button.home-project-inventory-row {
|
|
|
1901
1867
|
margin-top: 0;
|
|
1902
1868
|
font-size: var(--type-body);
|
|
1903
1869
|
line-height: var(--leading-body);
|
|
1904
|
-
color: var(--color-
|
|
1870
|
+
color: var(--color-legend);
|
|
1905
1871
|
}
|
|
1906
1872
|
|
|
1907
1873
|
.home-hero-summary p {
|
|
@@ -24,7 +24,7 @@ module Pandorga
|
|
|
24
24
|
|
|
25
25
|
gem "pandorga", "~> 1.3"
|
|
26
26
|
# Local checkout: gem "pandorga", path: "../pandorga"
|
|
27
|
-
# GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v1.
|
|
27
|
+
# GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v1.11.0"
|
|
28
28
|
gem "csv"
|
|
29
29
|
gem "base64"
|
|
30
30
|
RUBY
|
data/lib/pandorga/version.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "project-inventory-row/rack card (PLT-AC-
|
|
2
|
+
"name": "project-inventory-row/rack card (PLT-AC-41)",
|
|
3
3
|
"block": "renderProjectInventoryRow",
|
|
4
4
|
"args": [
|
|
5
5
|
{
|
|
@@ -25,28 +25,25 @@
|
|
|
25
25
|
"selectors": [
|
|
26
26
|
"button.home-project-inventory-row",
|
|
27
27
|
"div.home-project-inventory-media",
|
|
28
|
-
"div.
|
|
28
|
+
"div.home-project-inventory-mark",
|
|
29
29
|
"div.home-project-inventory-code",
|
|
30
30
|
"span.home-project-inventory-index",
|
|
31
31
|
"h3.home-project-inventory-title",
|
|
32
|
-
"
|
|
33
|
-
"span.home-project-inventory-glyph",
|
|
34
|
-
"div.home-project-inventory-resources",
|
|
35
|
-
"span.home-project-inventory-resource"
|
|
32
|
+
"span.home-project-inventory-glyph"
|
|
36
33
|
],
|
|
37
34
|
"forbidden": [
|
|
38
35
|
"ledger-card-stretch",
|
|
39
|
-
"role=\"button\""
|
|
36
|
+
"role=\"button\"",
|
|
37
|
+
"ledger-poster",
|
|
38
|
+
"home-project-inventory-copy",
|
|
39
|
+
"home-project-inventory-resources",
|
|
40
|
+
"home-project-inventory-resource"
|
|
40
41
|
],
|
|
41
42
|
"contains": [
|
|
42
43
|
">02<",
|
|
43
44
|
">Simulation<",
|
|
44
45
|
">ARCO<",
|
|
45
|
-
">A city-scale race replayed from telemetry.<",
|
|
46
46
|
">route<",
|
|
47
|
-
">code<",
|
|
48
|
-
">article<",
|
|
49
|
-
">public<",
|
|
50
47
|
"aria-pressed=\"false\"",
|
|
51
48
|
"data-slug=\"arco\""
|
|
52
49
|
]
|
|
@@ -226,6 +226,63 @@ sheets.each do |path|
|
|
|
226
226
|
end
|
|
227
227
|
end
|
|
228
228
|
|
|
229
|
+
# PLT-AC-41. Home project inventory rows use the square icon mark, two-line
|
|
230
|
+
# code + title body, and no tagline preview or attached-resource icons; on
|
|
231
|
+
# desktop the specimen (35%, min 20rem) and rack (45%, min 21rem) start at the
|
|
232
|
+
# band's left edge across one --space-xl gap, the specimen sets the bench
|
|
233
|
+
# height, rack titles use --type-card, the specimen strip wraps instead of
|
|
234
|
+
# clipping, and below 900px the rack tile glyphs are 1.375rem.
|
|
235
|
+
home_sheet = strip_comments(File.read(CSS_ROOT.join("ledger/home.css")))
|
|
236
|
+
%w[
|
|
237
|
+
home-project-inventory-copy
|
|
238
|
+
home-project-inventory-resources
|
|
239
|
+
home-project-inventory-resource
|
|
240
|
+
].each do |retired_home|
|
|
241
|
+
if home_sheet.match?(/[.#]#{Regexp.escape(retired_home)}\b/)
|
|
242
|
+
errors << "assets/css/ledger/home.css: .#{retired_home} must be removed (PLT-AC-41)"
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
require_declarations(
|
|
246
|
+
errors, "assets/css/ledger/home.css", home_sheet,
|
|
247
|
+
".home-project-inventory-media", ["aspect-ratio: 1 / 1"]
|
|
248
|
+
)
|
|
249
|
+
require_declarations(
|
|
250
|
+
errors, "assets/css/ledger/home.css", home_sheet,
|
|
251
|
+
".home-projects-bench", ["grid-template-columns: max(35%, 20rem) max(45%, 21rem)", "column-gap: var(--space-xl)", "justify-content: start", "--home-inventory-row-max: calc(1.6 * var(--home-inventory-row-min))"]
|
|
252
|
+
)
|
|
253
|
+
require_declarations(
|
|
254
|
+
errors, "assets/css/ledger/home.css", home_sheet,
|
|
255
|
+
".home-specimen-strip .ledger-meta", ["display: block", "white-space: normal"]
|
|
256
|
+
)
|
|
257
|
+
require_declarations(
|
|
258
|
+
errors, "assets/css/ledger/home.css", home_sheet,
|
|
259
|
+
".home-project-inventory-title", ["font-size: var(--type-card)"]
|
|
260
|
+
)
|
|
261
|
+
require_declarations(
|
|
262
|
+
errors, "assets/css/ledger/home.css", home_sheet,
|
|
263
|
+
".home-project-inventory-row .home-project-inventory-glyph", ["font-size: 1.375rem"]
|
|
264
|
+
)
|
|
265
|
+
[
|
|
266
|
+
"45.6%",
|
|
267
|
+
"minmax(0, 7fr)",
|
|
268
|
+
"--home-inventory-min-height",
|
|
269
|
+
"calc(var(--type-card) - 2pt)"
|
|
270
|
+
].each do |forbidden_home|
|
|
271
|
+
if home_sheet.include?(forbidden_home)
|
|
272
|
+
errors << "assets/css/ledger/home.css: #{forbidden_home} must be removed (PLT-AC-41)"
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# PLT-AC-23. The home hero summary uses --color-legend, the ink of the other
|
|
277
|
+
# description texts.
|
|
278
|
+
require_declarations(
|
|
279
|
+
errors, "assets/css/ledger/home.css", home_sheet,
|
|
280
|
+
".home-hero-summary", ["color: var(--color-legend)"]
|
|
281
|
+
)
|
|
282
|
+
if declaration_bodies(home_sheet, ".home-hero-summary").any? { |body| declarations(body).include?("color:var(--color-on-surface)") }
|
|
283
|
+
errors << "assets/css/ledger/home.css: .home-hero-summary must use --color-legend (PLT-AC-23)"
|
|
284
|
+
end
|
|
285
|
+
|
|
229
286
|
fail!("CSS structure problems:\n - #{errors.join("\n - ")}") unless errors.empty?
|
|
230
287
|
|
|
231
288
|
puts " #{sheets.size} stylesheet(s) balanced, no conflict markers."
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Refs: PLT-AC-42 — home listing bands emit kicker, title, and note slots in both normal and agnostic builds.
|
|
4
|
+
require "pathname"
|
|
5
|
+
require "open3"
|
|
6
|
+
require "fileutils"
|
|
7
|
+
require "tmpdir"
|
|
8
|
+
|
|
9
|
+
ROOT = Pathname.new(__dir__).join("../..").expand_path
|
|
10
|
+
EXE = ROOT.join("exe/pandorga")
|
|
11
|
+
EXAMPLE = ROOT.join("examples/full")
|
|
12
|
+
|
|
13
|
+
def fail!(message)
|
|
14
|
+
warn "FAIL test-home-band-intro: #{message}"
|
|
15
|
+
exit 1
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run!(*cmd, chdir:)
|
|
19
|
+
env = {
|
|
20
|
+
"RUBYOPT" => "-I#{ROOT.join('lib')}",
|
|
21
|
+
"PANDORGA_SITE_ROOT" => chdir.to_s,
|
|
22
|
+
"CONTENT_API_BASE_URL" => "",
|
|
23
|
+
"CONTENT_EXPORT_SKIP_VALIDATE" => "1",
|
|
24
|
+
"JEKYLL_ENV" => "production"
|
|
25
|
+
}
|
|
26
|
+
stdout, stderr, status = Open3.capture3(env, *cmd, chdir: chdir.to_s)
|
|
27
|
+
return [stdout, stderr] if status.success?
|
|
28
|
+
|
|
29
|
+
fail!("#{cmd.join(' ')} (cwd=#{chdir})\n#{stdout}\n#{stderr}")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def listing_sections(html)
|
|
33
|
+
sections = []
|
|
34
|
+
html.scan(/<section\b[^>]*\bdata-home-kind="([^"]+)"[^>]*>.*?<\/section>/m) do
|
|
35
|
+
section = Regexp.last_match(0)
|
|
36
|
+
kind = Regexp.last_match(1)
|
|
37
|
+
next if kind == "index"
|
|
38
|
+
|
|
39
|
+
key = section[/\bdata-home-key="([^"]+)"/, 1] || kind
|
|
40
|
+
banner_idx = section.index('data-content-home-section="')
|
|
41
|
+
fail!("section #{key} missing data-content-home-section") unless banner_idx
|
|
42
|
+
|
|
43
|
+
sections << [key, section[banner_idx..]]
|
|
44
|
+
end
|
|
45
|
+
sections
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
run!("bundle", "install", "--quiet", chdir: EXAMPLE) if EXAMPLE.join("Gemfile").file?
|
|
49
|
+
run!("ruby", EXE.to_s, "export", "content", "_content_json", chdir: EXAMPLE)
|
|
50
|
+
|
|
51
|
+
Dir.mktmpdir("pandorga-home-band-normal-") do |normal_dir|
|
|
52
|
+
run!("bundle", "exec", "jekyll", "build", "--quiet", "-d", normal_dir, chdir: EXAMPLE)
|
|
53
|
+
html = File.read(File.join(normal_dir, "index.html"))
|
|
54
|
+
sections = listing_sections(html)
|
|
55
|
+
fail!("normal build produced no listing sections") if sections.empty?
|
|
56
|
+
|
|
57
|
+
sections.each do |key, slice|
|
|
58
|
+
%w[data-content-section-kicker data-content-section-title data-content-section-note].each do |attr|
|
|
59
|
+
fail!("normal build band #{key} missing #{attr}") unless slice.include?(attr)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
projects = sections.assoc("projects")&.last
|
|
64
|
+
fail!("normal build missing projects band") unless projects
|
|
65
|
+
unless projects.include?("Portfolio systems in the full example.")
|
|
66
|
+
fail!("normal build projects band missing note text from headers.yml")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Dir.mktmpdir("pandorga-home-band-agnostic-") do |agnostic_dir|
|
|
71
|
+
agnostic_cfg = File.join(agnostic_dir, "agnostic.yml")
|
|
72
|
+
File.write(agnostic_cfg, "shell_content_agnostic: true\n")
|
|
73
|
+
out_dir = File.join(agnostic_dir, "site")
|
|
74
|
+
run!(
|
|
75
|
+
"bundle", "exec", "jekyll", "build", "--quiet",
|
|
76
|
+
"--config", "_config.yml,#{agnostic_cfg}",
|
|
77
|
+
"-d", out_dir,
|
|
78
|
+
chdir: EXAMPLE
|
|
79
|
+
)
|
|
80
|
+
html = File.read(File.join(out_dir, "index.html"))
|
|
81
|
+
sections = listing_sections(html)
|
|
82
|
+
fail!("agnostic build produced no listing sections") if sections.empty?
|
|
83
|
+
|
|
84
|
+
sections.each do |key, slice|
|
|
85
|
+
%w[data-content-section-kicker data-content-section-title data-content-section-note].each do |attr|
|
|
86
|
+
fail!("agnostic build band #{key} missing #{attr}") unless slice.include?(attr)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
projects = sections.assoc("projects")&.last
|
|
91
|
+
fail!("agnostic build missing projects band") unless projects
|
|
92
|
+
unless projects.include?("data-content-section-note></p>")
|
|
93
|
+
fail!("agnostic build projects band note slot is not empty")
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
puts "PASS test-home-band-intro"
|
data/scripts/validate.sh
CHANGED
|
@@ -55,6 +55,7 @@ TESTS=(
|
|
|
55
55
|
scripts/test/test-r2-json-sync.rb
|
|
56
56
|
scripts/test/test-writing-slug-limits.rb
|
|
57
57
|
scripts/test/test-examples-build.rb
|
|
58
|
+
scripts/test/test-home-band-intro.rb
|
|
58
59
|
scripts/test/test-validate-allowlist.rb
|
|
59
60
|
scripts/test/test-shell-a11y.rb
|
|
60
61
|
scripts/test/test-theme-contrast.rb
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pandorga
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The Pandorga contributors
|
|
@@ -435,6 +435,7 @@ files:
|
|
|
435
435
|
- scripts/test/test-hero-summary.rb
|
|
436
436
|
- scripts/test/test-home-articles.rb
|
|
437
437
|
- scripts/test/test-home-band-art.rb
|
|
438
|
+
- scripts/test/test-home-band-intro.rb
|
|
438
439
|
- scripts/test/test-home-lists.rb
|
|
439
440
|
- scripts/test/test-hydrate-reserve.rb
|
|
440
441
|
- scripts/test/test-install-functions.rb
|