pandorga 2.0.3 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/_data/themes/shared.yml +12 -12
- data/_includes/content-runtime/43-blocks.html +1 -0
- data/_includes/home/band-listing.html +4 -1
- data/_includes/theme/theme-vars-block.liquid +7 -3
- data/_layouts/shell.html +25 -0
- data/assets/css/base.css +17 -2
- data/assets/css/ledger/chrome.css +12 -0
- data/assets/css/ledger/detail.css +28 -8
- data/assets/css/ledger/home.css +55 -48
- data/assets/css/ledger/projects.css +0 -1
- data/examples/full/_config.yml +3 -3
- data/lib/pandorga/commands/new.rb +1 -1
- data/lib/pandorga/fonts.rb +5 -8
- data/lib/pandorga/registry/schema.yml +8 -2
- data/lib/pandorga/version.rb +1 -1
- data/scripts/test/render-parity/blocks/project-inventory-row.json +1 -0
- data/scripts/test/test-css-structure.rb +19 -1
- data/scripts/test/test-examples-build.rb +2 -2
- data/scripts/test/test-fonts.rb +9 -8
- data/scripts/test/test-hero-summary.rb +1 -1
- data/scripts/test/test-home-articles.rb +2 -2
- data/scripts/test/test-mobile-density-home-measure.rb +161 -0
- data/scripts/test/test-v3-layout.rb +1 -1
- data/scripts/validate.sh +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3dd3df9d18ed1ac1a99e07aecfe17e5776ccf46e35ca30cb4c87dc8e9ec6a00f
|
|
4
|
+
data.tar.gz: c87fa62d1dcdd8ddc1b1f00671b83558e0114b4d28a2de376eb653251740a7a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f75cd5a291e0678b90997a207a73bdd3401e76e3de3d4bcbd732323f46e87f11b4aa0075a46a61e35378a49d79ae7acb97d521793ca04a5705e5fe4a5d6d7381
|
|
7
|
+
data.tar.gz: 279e4ed500471c6b7a74129bb9a18648c4c21054aa9b02af7f2794f9eada23afe7249c23b2de2f6555301d3d520b4caba60c7c30e3620eae8aaf8409905900d2
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.1.0] — 2026-10-28
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Site-configurable body weights under `pandorga.fonts.body_weight` (desktop default 300) and `pandorga.fonts.body_weight_mobile` (mobile default 350) wired into CSS custom properties (`--weight-body` and `--weight-body-mobile`) (PLT-AC-57).
|
|
10
|
+
- Static Marcellus SC label **“Explore projects”** above the Home Projects inventory (PLT-AC-61).
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Default resolved typography stack switches to four families: Ancizar Serif for titles and top-bar wordmark, Ancizar Sans for body and taglines, Marcellus SC for labels, and Courier Prime for code. Cinzel Decorative and Newsreader defaults are removed from the default stylesheet request (PLT-AC-56).
|
|
15
|
+
- `--font-content-title` uses `--font-title` (Ancizar Serif) instead of body face (PLT-AC-56).
|
|
16
|
+
- On the home hero, `.home-hero-family` renders in italic, primary color, with a highlighter mark wash scaled to ~40% intensity of detail action buttons, while `.home-hero-given` stays upright in default ink (PLT-AC-58).
|
|
17
|
+
- Heading and title selectors in ledger CSS no longer apply `text-transform: uppercase`, and title italics are restricted to `.home-hero-family` and `.section-title em` / `.page-header-title em` (PLT-AC-59, PLT-AC-60).
|
|
18
|
+
- On viewports at or above 900px, the Home Projects bench places the inventory column to the left of the specimen. Inventory buttons become logo-only with enlarged icon marks, keeping accessible labels (`aria-label`) and tooltips on hover/focus (`title`) (PLT-AC-61).
|
|
19
|
+
|
|
20
|
+
## [2.0.4] — 2026-10-28
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Mobile article top compresses dates and action buttons into one band and closes the gap before the reading column.
|
|
25
|
+
- Mobile top bar is shorter and hides on scroll-down (shows on scroll-up, focus, or Explore open).
|
|
26
|
+
- Home hero summary uses a ~65ch measure on desktop.
|
|
27
|
+
- Home section "View all" sits beside the section title instead of at the far right of the band.
|
|
28
|
+
|
|
5
29
|
## [2.0.3] — 2026-10-25
|
|
6
30
|
|
|
7
31
|
### Fixed
|
data/_data/themes/shared.yml
CHANGED
|
@@ -5,27 +5,27 @@
|
|
|
5
5
|
# Consumed by theme-context.liquid → theme-vars-block.liquid / font-loader.
|
|
6
6
|
|
|
7
7
|
fonts:
|
|
8
|
-
title: "
|
|
9
|
-
subtitle: "
|
|
8
|
+
title: "Ancizar Serif"
|
|
9
|
+
subtitle: "Ancizar Sans"
|
|
10
10
|
label: "Marcellus SC"
|
|
11
|
-
body: "
|
|
11
|
+
body: "Ancizar Sans"
|
|
12
12
|
mono: "Courier Prime"
|
|
13
13
|
code: "Courier Prime"
|
|
14
|
-
cv_body: "
|
|
14
|
+
cv_body: "Ancizar Sans"
|
|
15
15
|
cv_body_weight: 400
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
body_fallback: "
|
|
19
|
-
subtitle_fallback: "serif"
|
|
16
|
+
body_weight: 300
|
|
17
|
+
body_weight_mobile: 350
|
|
18
|
+
body_fallback: "sans-serif"
|
|
19
|
+
subtitle_fallback: "sans-serif"
|
|
20
20
|
title_fallback: "serif"
|
|
21
21
|
label_fallback: "serif"
|
|
22
|
-
google_title_spec: "wght@400;
|
|
23
|
-
google_subtitle_spec: "
|
|
22
|
+
google_title_spec: "ital,wght@0,400;0,600;1,400"
|
|
23
|
+
google_subtitle_spec: "wght@300;350;400;600"
|
|
24
24
|
google_label_spec: "wght@400;700"
|
|
25
|
-
google_body_spec: "wght@300;400;
|
|
25
|
+
google_body_spec: "wght@300;350;400;600"
|
|
26
26
|
google_mono_spec: "ital,wght@0,400;0,700;1,400;1,700"
|
|
27
27
|
google_code_spec: "ital,wght@0,300;0,400;0,700;1,400;1,700"
|
|
28
|
-
google_cv_body_spec: "wght@300;400;
|
|
28
|
+
google_cv_body_spec: "wght@300;350;400;600"
|
|
29
29
|
|
|
30
30
|
motion:
|
|
31
31
|
duration_fast: "0.15s"
|
|
@@ -55,8 +55,11 @@
|
|
|
55
55
|
data-home-list
|
|
56
56
|
data-tag-base="{{ _path }}"
|
|
57
57
|
>
|
|
58
|
+
<div class="home-projects-inventory-wrap">
|
|
59
|
+
<span class="home-projects-inventory-label font-label">Explore projects</span>
|
|
60
|
+
<div class="home-project-inventory" data-home-inventory role="region" aria-label="Project inventory"></div>
|
|
61
|
+
</div>
|
|
58
62
|
<div class="home-project-specimen-slot" data-home-specimen></div>
|
|
59
|
-
<div class="home-project-inventory" data-home-inventory role="region" aria-label="Project inventory"></div>
|
|
60
63
|
</div>
|
|
61
64
|
{%- else -%}
|
|
62
65
|
<div
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
{% assign color_secondary = tc.secondary | default: "#287445" %}
|
|
20
20
|
{% assign color_tertiary = tc.tertiary | default: "#746f30" %}
|
|
21
21
|
{% assign font_cv_body_weight = sf.cv_body_weight | default: 400 %}
|
|
22
|
+
{% assign site_fonts = site.pandorga.fonts %}
|
|
23
|
+
{% assign font_body_weight = site_fonts.body_weight | default: sf.body_weight | default: 300 %}
|
|
24
|
+
{% assign font_body_weight_mobile = site_fonts.body_weight_mobile | default: sf.body_weight_mobile | default: 350 %}
|
|
22
25
|
{% assign bg_secondary = tn.background_secondary | default: tn.surface_container_low | default: "#f5f5f5" %}
|
|
23
26
|
{% assign surface_value = tn.surface | default: "#ffffff" %}
|
|
24
27
|
{% assign surface_variant_value = tn.surface_variant | default: "#e2e2e2" %}
|
|
@@ -30,10 +33,11 @@
|
|
|
30
33
|
--font-body: {{ site | pandorga_font_stack: "body" }};
|
|
31
34
|
--font-cv-body: {{ site | pandorga_font_stack: "cv_body" }};
|
|
32
35
|
--font-cv-body-weight: {{ font_cv_body_weight }};
|
|
36
|
+
--weight-body: {{ font_body_weight }};
|
|
37
|
+
--weight-body-mobile: {{ font_body_weight_mobile }};
|
|
33
38
|
--font-serif: {{ site | pandorga_font_stack: "title" }};
|
|
34
|
-
/* Content titles
|
|
35
|
-
|
|
36
|
-
--font-content-title: var(--font-body);
|
|
39
|
+
/* Content titles use the title face (Ancizar Serif). */
|
|
40
|
+
--font-content-title: var(--font-title);
|
|
37
41
|
--font-sans: var(--font-body);
|
|
38
42
|
--font-mono: {{ site | pandorga_font_stack: "mono" }};
|
|
39
43
|
--font-code: {{ site | pandorga_font_stack: "code" }};
|
data/_layouts/shell.html
CHANGED
|
@@ -287,11 +287,13 @@
|
|
|
287
287
|
var wrap = document.getElementById('site-menu');
|
|
288
288
|
var btn = document.getElementById('mobile-menu-btn');
|
|
289
289
|
var menu = document.getElementById('mobile-menu');
|
|
290
|
+
var topbar = document.querySelector('.site-topbar');
|
|
290
291
|
if (!wrap || !btn || !menu) return;
|
|
291
292
|
|
|
292
293
|
function setOpen(open) {
|
|
293
294
|
menu.classList.toggle('hidden', !open);
|
|
294
295
|
btn.setAttribute('aria-expanded', String(open));
|
|
296
|
+
if (open && topbar) topbar.classList.remove('topbar--hidden');
|
|
295
297
|
}
|
|
296
298
|
|
|
297
299
|
btn.addEventListener('click', function (event) {
|
|
@@ -304,6 +306,29 @@
|
|
|
304
306
|
document.addEventListener('keydown', function (event) {
|
|
305
307
|
if (event.key === 'Escape') setOpen(false);
|
|
306
308
|
});
|
|
309
|
+
|
|
310
|
+
if (!topbar) return;
|
|
311
|
+
var lastY = window.scrollY || 0;
|
|
312
|
+
var threshold = 8;
|
|
313
|
+
|
|
314
|
+
function onScroll() {
|
|
315
|
+
var y = window.scrollY || 0;
|
|
316
|
+
var diff = y - lastY;
|
|
317
|
+
|
|
318
|
+
if (y <= 32 || !menu.classList.contains('hidden')) {
|
|
319
|
+
topbar.classList.remove('topbar--hidden');
|
|
320
|
+
} else if (diff > threshold) {
|
|
321
|
+
topbar.classList.add('topbar--hidden');
|
|
322
|
+
} else if (diff < -threshold) {
|
|
323
|
+
topbar.classList.remove('topbar--hidden');
|
|
324
|
+
}
|
|
325
|
+
lastY = y;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
329
|
+
topbar.addEventListener('focusin', function () {
|
|
330
|
+
topbar.classList.remove('topbar--hidden');
|
|
331
|
+
});
|
|
307
332
|
})();
|
|
308
333
|
</script>
|
|
309
334
|
|
data/assets/css/base.css
CHANGED
|
@@ -39,6 +39,15 @@
|
|
|
39
39
|
line-gap-override: 0%;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
@font-face {
|
|
43
|
+
font-family: "Pandorga Ancizar Sans Fallback";
|
|
44
|
+
src: local("Arial"), local("Helvetica"), local("Liberation Sans"), local("sans-serif");
|
|
45
|
+
size-adjust: 100%;
|
|
46
|
+
ascent-override: 90%;
|
|
47
|
+
descent-override: 25%;
|
|
48
|
+
line-gap-override: 0%;
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
@font-face {
|
|
43
52
|
font-family: "Pandorga Label Fallback";
|
|
44
53
|
src: local("Times New Roman"), local("Times"), local("Liberation Serif");
|
|
@@ -163,7 +172,7 @@
|
|
|
163
172
|
html {
|
|
164
173
|
font-size: 110%;
|
|
165
174
|
|
|
166
|
-
/* Display —
|
|
175
|
+
/* Display — Ancizar Serif */
|
|
167
176
|
--type-display: 2.9015rem;
|
|
168
177
|
--type-display-lg: 3.6515rem;
|
|
169
178
|
--type-page: 2.4015rem;
|
|
@@ -173,7 +182,7 @@ html {
|
|
|
173
182
|
/* Phone steps for home band titles and card titles (PLT-AC-49). */
|
|
174
183
|
--type-section-sm: 1.375rem;
|
|
175
184
|
|
|
176
|
-
/* Content titles —
|
|
185
|
+
/* Content titles — Ancizar Serif */
|
|
177
186
|
--type-title: 2.20165rem;
|
|
178
187
|
--type-title-lg: 2.64165rem;
|
|
179
188
|
--type-card: 1.4015rem;
|
|
@@ -226,6 +235,12 @@ html {
|
|
|
226
235
|
}
|
|
227
236
|
}
|
|
228
237
|
|
|
238
|
+
@media (max-width: 767px) {
|
|
239
|
+
:root, [data-theme] {
|
|
240
|
+
--weight-body: var(--weight-body-mobile, 350);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
229
244
|
/* `html body`, not `body`: Tailwind's Preflight ships `body { line-height:
|
|
230
245
|
inherit }` from the CDN <style> that loads after this file, so a bare `body`
|
|
231
246
|
selector here loses the tie on source order and the leading silently becomes
|
|
@@ -35,6 +35,18 @@
|
|
|
35
35
|
-webkit-backdrop-filter: blur(10px);
|
|
36
36
|
backdrop-filter: blur(10px);
|
|
37
37
|
border-bottom: 1px solid var(--border-gentle-divider);
|
|
38
|
+
transition: transform var(--motion-duration-fast) var(--motion-ease-default);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@media (max-width: 767px) {
|
|
42
|
+
.site-topbar > div {
|
|
43
|
+
padding-top: 0.625rem;
|
|
44
|
+
padding-bottom: 0.625rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.site-topbar.topbar--hidden {
|
|
48
|
+
transform: translateY(-100%);
|
|
49
|
+
}
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
/* Boxed controls for theme and mobile menu toggles. */
|
|
@@ -694,7 +694,8 @@ aside:has(> .text-detail-sidebar) {
|
|
|
694
694
|
|
|
695
695
|
.text-detail-sidebar .article-toc--disclosure,
|
|
696
696
|
.projects-dossier .article-toc--disclosure {
|
|
697
|
-
margin-top: var(--space-
|
|
697
|
+
margin-top: var(--space-xs);
|
|
698
|
+
margin-bottom: var(--space-xs);
|
|
698
699
|
}
|
|
699
700
|
}
|
|
700
701
|
|
|
@@ -1184,7 +1185,7 @@ nav.text-page-nav:not(:has(a)) {
|
|
|
1184
1185
|
}
|
|
1185
1186
|
|
|
1186
1187
|
.grid.text-detail-grid {
|
|
1187
|
-
gap: var(--space-
|
|
1188
|
+
gap: var(--space-xs);
|
|
1188
1189
|
}
|
|
1189
1190
|
|
|
1190
1191
|
/* The reading column used to add its own top margin on top of this gap,
|
|
@@ -1232,12 +1233,32 @@ nav.text-page-nav:not(:has(a)) {
|
|
|
1232
1233
|
padding-bottom: var(--space-md);
|
|
1233
1234
|
}
|
|
1234
1235
|
|
|
1235
|
-
/*
|
|
1236
|
-
flex gap was stacking another --space-md on the action rule, so the
|
|
1237
|
-
date sat high in that band. Drop the gap and the action margin is the
|
|
1238
|
-
matching --space-sm under the text. */
|
|
1236
|
+
/* On mobile, date row and action buttons share one compact horizontal band on one non-wrapping row. */
|
|
1239
1237
|
.text-detail-sidebar .article-meta {
|
|
1240
|
-
|
|
1238
|
+
display: flex;
|
|
1239
|
+
flex-direction: row;
|
|
1240
|
+
flex-wrap: nowrap;
|
|
1241
|
+
align-items: center;
|
|
1242
|
+
justify-content: space-between;
|
|
1243
|
+
gap: var(--space-xs) var(--space-sm);
|
|
1244
|
+
margin-bottom: 0;
|
|
1245
|
+
padding-bottom: 0;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
.text-detail-sidebar .article-meta > div {
|
|
1249
|
+
margin: 0;
|
|
1250
|
+
padding: 0;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
.text-detail-sidebar .article-meta .date-evolution > span {
|
|
1254
|
+
border-top: 0;
|
|
1255
|
+
padding-top: 0;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
.text-detail-sidebar .article-meta .detail-actions {
|
|
1259
|
+
margin-top: 0;
|
|
1260
|
+
padding-top: 0;
|
|
1261
|
+
border-top: 0;
|
|
1241
1262
|
}
|
|
1242
1263
|
|
|
1243
1264
|
.text-detail-sidebar .article-sidebar-description,
|
|
@@ -1284,7 +1305,6 @@ nav.text-page-nav:not(:has(a)) {
|
|
|
1284
1305
|
font-family: var(--font-label);
|
|
1285
1306
|
font-size: var(--type-label-lg);
|
|
1286
1307
|
letter-spacing: 0.08em;
|
|
1287
|
-
text-transform: uppercase;
|
|
1288
1308
|
color: var(--color-on-surface);
|
|
1289
1309
|
font-weight: 600;
|
|
1290
1310
|
margin: 0 0 var(--space-md);
|
data/assets/css/ledger/home.css
CHANGED
|
@@ -169,13 +169,22 @@
|
|
|
169
169
|
max-width: 100%;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
.home-hero-given
|
|
173
|
-
.home-hero-family {
|
|
172
|
+
.home-hero-given {
|
|
174
173
|
display: inline;
|
|
175
174
|
}
|
|
176
175
|
|
|
177
176
|
.home-hero-family {
|
|
177
|
+
display: inline;
|
|
178
|
+
font-style: italic;
|
|
178
179
|
color: var(--color-primary);
|
|
180
|
+
--mark-ink: var(--color-primary);
|
|
181
|
+
--mark-1: calc(var(--mark-quiet-a) * 0.4);
|
|
182
|
+
--mark-2: calc(var(--mark-quiet-b) * 0.4);
|
|
183
|
+
--mark-3: calc(var(--mark-quiet-c) * 0.4);
|
|
184
|
+
background: linear-gradient(var(--mk-angle, 104deg), transparent var(--mk-s0, 0.9%), color-mix(in srgb, var(--mark-ink) var(--mark-1), transparent) var(--mk-s1, 2.6%), color-mix(in srgb, var(--mark-ink) var(--mark-2), transparent) var(--mk-mid, 58%), color-mix(in srgb, var(--mark-ink) var(--mark-3), transparent) var(--mk-end, 98%), transparent 100%) 0 var(--mk-y, 50%) / 100% var(--mk-h, 100%) no-repeat;
|
|
185
|
+
-webkit-box-decoration-break: clone;
|
|
186
|
+
box-decoration-break: clone;
|
|
187
|
+
padding: 0 0.1em;
|
|
179
188
|
}
|
|
180
189
|
|
|
181
190
|
@media (min-width: 640px) {
|
|
@@ -220,6 +229,12 @@
|
|
|
220
229
|
line-height: var(--leading-body);
|
|
221
230
|
}
|
|
222
231
|
|
|
232
|
+
@media (min-width: 768px) {
|
|
233
|
+
.home-hero-summary {
|
|
234
|
+
max-width: 65ch;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
223
238
|
/* No empty min-height. A guessed line count shifts the call to action
|
|
224
239
|
when the summary is shorter or taller than the reserve. The paragraph
|
|
225
240
|
is in the HTML when content/pages/home/summary.md or
|
|
@@ -415,7 +430,7 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
415
430
|
The banner is a 2-column grid so the lead's children join the template. */
|
|
416
431
|
.home-band:not(.home-band--hero) .ledger-section-banner {
|
|
417
432
|
display: grid;
|
|
418
|
-
grid-template-columns:
|
|
433
|
+
grid-template-columns: auto auto 1fr;
|
|
419
434
|
column-gap: var(--space-md);
|
|
420
435
|
row-gap: var(--space-xs);
|
|
421
436
|
align-items: baseline;
|
|
@@ -640,7 +655,7 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
640
655
|
|
|
641
656
|
[data-home-kind="articles"] .home-card-grid .card-title,
|
|
642
657
|
[data-home-kind="articles"] .home-card-grid .ledger-card-title {
|
|
643
|
-
font-size:
|
|
658
|
+
font-size: var(--type-card);
|
|
644
659
|
}
|
|
645
660
|
|
|
646
661
|
/* Phone only: the featured card's text sat against the top rule. One
|
|
@@ -658,6 +673,7 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
658
673
|
.home-project-specimen .ledger-card-title,
|
|
659
674
|
.home-project-inventory-title {
|
|
660
675
|
font-family: var(--font-content-title);
|
|
676
|
+
font-size: var(--type-card);
|
|
661
677
|
font-weight: var(--weight-title);
|
|
662
678
|
font-synthesis: none;
|
|
663
679
|
}
|
|
@@ -983,6 +999,16 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
983
999
|
gap: var(--space-md);
|
|
984
1000
|
}
|
|
985
1001
|
|
|
1002
|
+
.home-projects-inventory-label {
|
|
1003
|
+
display: block;
|
|
1004
|
+
font-family: var(--font-label);
|
|
1005
|
+
font-size: var(--type-label);
|
|
1006
|
+
font-weight: var(--weight-label);
|
|
1007
|
+
letter-spacing: 0.04em;
|
|
1008
|
+
color: var(--color-on-surface-variant);
|
|
1009
|
+
margin-bottom: var(--space-xs);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
986
1012
|
.home-project-inventory {
|
|
987
1013
|
order: -1;
|
|
988
1014
|
}
|
|
@@ -1307,10 +1333,7 @@ button.home-project-inventory-row {
|
|
|
1307
1333
|
|
|
1308
1334
|
@media (min-width: 900px) {
|
|
1309
1335
|
|
|
1310
|
-
/* The
|
|
1311
|
-
16:9 plate at 60% of the 7:5 split's height; the rack is 45% (at least
|
|
1312
|
-
21rem); the pair starts at the band's left edge and leaves the rest of
|
|
1313
|
-
the row blank. The specimen sets the bench height; rack rows share it. */
|
|
1336
|
+
/* The inventory rack sits on the left (45%, min 21rem), and the specimen on the right (35%, min 20rem). */
|
|
1314
1337
|
.home-projects-bench {
|
|
1315
1338
|
--home-inventory-mark-size: 3rem;
|
|
1316
1339
|
--home-inventory-row-min: calc(
|
|
@@ -1320,12 +1343,19 @@ button.home-project-inventory-row {
|
|
|
1320
1343
|
single row to the full plate. */
|
|
1321
1344
|
--home-inventory-row-max: calc(1.6 * var(--home-inventory-row-min));
|
|
1322
1345
|
display: grid;
|
|
1323
|
-
grid-template-columns: max(
|
|
1346
|
+
grid-template-columns: max(45%, 21rem) max(35%, 20rem);
|
|
1324
1347
|
column-gap: var(--space-xl);
|
|
1325
1348
|
justify-content: start;
|
|
1326
1349
|
align-items: stretch;
|
|
1327
1350
|
}
|
|
1328
1351
|
|
|
1352
|
+
.home-projects-inventory-wrap {
|
|
1353
|
+
display: flex;
|
|
1354
|
+
flex-direction: column;
|
|
1355
|
+
gap: var(--space-xs);
|
|
1356
|
+
width: 100%;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1329
1359
|
.home-project-inventory {
|
|
1330
1360
|
order: 0;
|
|
1331
1361
|
display: flex;
|
|
@@ -1335,7 +1365,6 @@ button.home-project-inventory-row {
|
|
|
1335
1365
|
width: 100%;
|
|
1336
1366
|
min-height: var(--home-specimen-height);
|
|
1337
1367
|
min-width: 0;
|
|
1338
|
-
min-height: 0;
|
|
1339
1368
|
overflow: hidden;
|
|
1340
1369
|
padding: var(--space-md);
|
|
1341
1370
|
background: var(--color-panel-contrast);
|
|
@@ -1348,7 +1377,7 @@ button.home-project-inventory-row {
|
|
|
1348
1377
|
.home-project-inventory-row {
|
|
1349
1378
|
flex: 1 1 0;
|
|
1350
1379
|
align-items: center;
|
|
1351
|
-
justify-content:
|
|
1380
|
+
justify-content: center;
|
|
1352
1381
|
gap: var(--space-md);
|
|
1353
1382
|
height: auto;
|
|
1354
1383
|
min-height: var(--home-inventory-row-min);
|
|
@@ -1367,7 +1396,12 @@ button.home-project-inventory-row {
|
|
|
1367
1396
|
box-shadow: inset 3px 0 0 var(--color-primary);
|
|
1368
1397
|
}
|
|
1369
1398
|
|
|
1370
|
-
/*
|
|
1399
|
+
/* Logo-only buttons on desktop: hide text body visually. */
|
|
1400
|
+
.home-project-inventory-body {
|
|
1401
|
+
display: none;
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
/* Square icon mark enlarged. */
|
|
1371
1405
|
.home-project-inventory-media {
|
|
1372
1406
|
display: block;
|
|
1373
1407
|
flex: 0 0 auto;
|
|
@@ -1396,50 +1430,17 @@ button.home-project-inventory-row {
|
|
|
1396
1430
|
}
|
|
1397
1431
|
|
|
1398
1432
|
.home-project-inventory-mark .material-symbols-outlined {
|
|
1399
|
-
font-size: 1.
|
|
1433
|
+
font-size: 1.75rem;
|
|
1400
1434
|
}
|
|
1401
1435
|
|
|
1402
1436
|
.home-project-inventory-mark--initial {
|
|
1403
1437
|
font-family: var(--font-content-title);
|
|
1404
|
-
font-size: var(--type-card);
|
|
1438
|
+
font-size: var(--type-card-lg);
|
|
1405
1439
|
font-weight: var(--weight-title);
|
|
1406
1440
|
line-height: 1;
|
|
1407
1441
|
color: var(--color-on-surface);
|
|
1408
1442
|
}
|
|
1409
1443
|
|
|
1410
|
-
.home-project-inventory-body {
|
|
1411
|
-
display: flex;
|
|
1412
|
-
flex-direction: column;
|
|
1413
|
-
justify-content: center;
|
|
1414
|
-
align-self: center;
|
|
1415
|
-
gap: 0.15rem;
|
|
1416
|
-
flex: 1 1 0;
|
|
1417
|
-
min-width: 0;
|
|
1418
|
-
padding-top: 0;
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
.home-project-inventory-code .ledger-meta {
|
|
1422
|
-
font-size: var(--type-label-lg);
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
.home-project-inventory-title {
|
|
1426
|
-
margin: 0;
|
|
1427
|
-
font-family: var(--font-content-title);
|
|
1428
|
-
font-size: var(--type-card);
|
|
1429
|
-
font-weight: var(--weight-title);
|
|
1430
|
-
line-height: 1.2;
|
|
1431
|
-
color: var(--color-on-surface);
|
|
1432
|
-
overflow: hidden;
|
|
1433
|
-
text-overflow: ellipsis;
|
|
1434
|
-
white-space: nowrap;
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
.home-project-inventory-row:hover .home-project-inventory-title,
|
|
1438
|
-
.home-project-inventory-row:focus-visible .home-project-inventory-title,
|
|
1439
|
-
.home-project-inventory-row.is-selected .home-project-inventory-title {
|
|
1440
|
-
color: var(--color-primary);
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
1444
|
/* Two classes: the glyph also carries .material-symbols-outlined, whose
|
|
1444
1445
|
font stylesheet loads after this one and sets display. */
|
|
1445
1446
|
.home-project-inventory-row .home-project-inventory-glyph {
|
|
@@ -1732,7 +1733,7 @@ button.home-project-inventory-row {
|
|
|
1732
1733
|
that names the person, says what he does and offers the full record, rather
|
|
1733
1734
|
than a name band followed by a summary band saying the same thing twice. */
|
|
1734
1735
|
|
|
1735
|
-
.home-hero-summary {
|
|
1736
|
+
.home-hero .home-hero-summary {
|
|
1736
1737
|
max-width: var(--reading-measure);
|
|
1737
1738
|
margin-top: 0;
|
|
1738
1739
|
font-size: var(--type-body);
|
|
@@ -1740,6 +1741,12 @@ button.home-project-inventory-row {
|
|
|
1740
1741
|
color: var(--color-legend);
|
|
1741
1742
|
}
|
|
1742
1743
|
|
|
1744
|
+
@media (min-width: 768px) {
|
|
1745
|
+
.home-hero .home-hero-summary {
|
|
1746
|
+
max-width: 65ch;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1743
1750
|
.home-hero-summary p {
|
|
1744
1751
|
margin-bottom: var(--space-md);
|
|
1745
1752
|
}
|
data/examples/full/_config.yml
CHANGED
|
@@ -19,10 +19,10 @@ pandorga:
|
|
|
19
19
|
home:
|
|
20
20
|
hero_art: page/hero-default.html
|
|
21
21
|
fonts:
|
|
22
|
-
title: "
|
|
22
|
+
title: "Ancizar Serif"
|
|
23
23
|
body:
|
|
24
|
-
family: "
|
|
25
|
-
weights: [300,
|
|
24
|
+
family: "Ancizar Sans"
|
|
25
|
+
weights: [300, 350, 400, 600]
|
|
26
26
|
mono:
|
|
27
27
|
family: "Courier Prime"
|
|
28
28
|
weights: [400, 700]
|
|
@@ -24,7 +24,7 @@ module Pandorga
|
|
|
24
24
|
|
|
25
25
|
gem "pandorga", "~> 2.0"
|
|
26
26
|
# Local checkout: gem "pandorga", path: "../pandorga"
|
|
27
|
-
# GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v2.0
|
|
27
|
+
# GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v2.1.0"
|
|
28
28
|
gem "csv"
|
|
29
29
|
gem "base64"
|
|
30
30
|
RUBY
|
data/lib/pandorga/fonts.rb
CHANGED
|
@@ -22,21 +22,18 @@ module Pandorga
|
|
|
22
22
|
"Cinzel" => "Pandorga Cinzel Fallback",
|
|
23
23
|
"Newsreader" => "Pandorga Body Fallback",
|
|
24
24
|
"Ancizar Serif" => "Pandorga Ancizar Fallback",
|
|
25
|
+
"Ancizar Sans" => "Pandorga Ancizar Sans Fallback",
|
|
25
26
|
"Marcellus SC" => "Pandorga Label Fallback"
|
|
26
27
|
}.freeze
|
|
27
28
|
|
|
28
29
|
DEFAULTS = {
|
|
29
|
-
title: { family: "
|
|
30
|
-
subtitle: {
|
|
31
|
-
family: "Newsreader",
|
|
32
|
-
spec: "ital,opsz,wght@0,6..72,300;0,6..72,400;0,6..72,500;0,6..72,600;0,6..72,700;1,6..72,300;1,6..72,400;1,6..72,500",
|
|
33
|
-
fallback: "serif"
|
|
34
|
-
},
|
|
30
|
+
title: { family: "Ancizar Serif", spec: "ital,wght@0,400;0,600;1,400", fallback: "serif" },
|
|
31
|
+
subtitle: { family: "Ancizar Sans", spec: "wght@300;350;400;600", fallback: "sans-serif" },
|
|
35
32
|
label: { family: "Marcellus SC", spec: "wght@400;700", fallback: "serif" },
|
|
36
|
-
body: { family: "
|
|
33
|
+
body: { family: "Ancizar Sans", spec: "wght@300;350;400;600", fallback: "sans-serif" },
|
|
37
34
|
mono: { family: "Courier Prime", spec: "ital,wght@0,400;0,700;1,400;1,700", fallback: MONO_FALLBACK },
|
|
38
35
|
code: { family: "Courier Prime", spec: "ital,wght@0,300;0,400;0,700;1,400;1,700", fallback: MONO_FALLBACK },
|
|
39
|
-
cv_body: { family: "
|
|
36
|
+
cv_body: { family: "Ancizar Sans", spec: "wght@300;350;400;600", fallback: "sans-serif" }
|
|
40
37
|
}.freeze
|
|
41
38
|
|
|
42
39
|
def resolve(site_fonts, theme_fonts = nil)
|
|
@@ -57,10 +57,10 @@ fonts:
|
|
|
57
57
|
keys:
|
|
58
58
|
title:
|
|
59
59
|
type: string or map
|
|
60
|
-
notes: Display face. Default family `
|
|
60
|
+
notes: Display face. Default family `Ancizar Serif`, weights 400, 600, italic 400
|
|
61
61
|
body:
|
|
62
62
|
type: string or map
|
|
63
|
-
notes: Reading face. Default family `
|
|
63
|
+
notes: Reading face. Default family `Ancizar Sans`, weights 300, 350, 400, and 600
|
|
64
64
|
mono:
|
|
65
65
|
type: string or map
|
|
66
66
|
notes: Monospace face. Default family `Courier Prime`, roman and italic at 400 and 700
|
|
@@ -70,6 +70,12 @@ fonts:
|
|
|
70
70
|
subtitle:
|
|
71
71
|
type: string or map
|
|
72
72
|
notes: Optional tagline face. Omitted, it follows `body` when the theme used the same family
|
|
73
|
+
body_weight:
|
|
74
|
+
type: integer
|
|
75
|
+
notes: Desktop body weight (default 300)
|
|
76
|
+
body_weight_mobile:
|
|
77
|
+
type: integer
|
|
78
|
+
notes: Mobile body weight (default 350)
|
|
73
79
|
|
|
74
80
|
taxonomies:
|
|
75
81
|
title: "`pandorga.taxonomies`"
|
data/lib/pandorga/version.rb
CHANGED
|
@@ -247,7 +247,7 @@ require_declarations(
|
|
|
247
247
|
)
|
|
248
248
|
require_declarations(
|
|
249
249
|
errors, "assets/css/ledger/home.css", home_sheet,
|
|
250
|
-
".home-projects-bench", ["grid-template-columns: max(
|
|
250
|
+
".home-projects-bench", ["grid-template-columns: max(45%, 21rem) max(35%, 20rem)", "column-gap: var(--space-xl)", "justify-content: start", "--home-inventory-row-max: calc(1.6 * var(--home-inventory-row-min))"]
|
|
251
251
|
)
|
|
252
252
|
require_declarations(
|
|
253
253
|
errors, "assets/css/ledger/home.css", home_sheet,
|
|
@@ -272,6 +272,24 @@ require_declarations(
|
|
|
272
272
|
end
|
|
273
273
|
end
|
|
274
274
|
|
|
275
|
+
# PLT-AC-60. Heading and title selectors in ledger CSS do not use text-transform: uppercase.
|
|
276
|
+
title_selector_pattern = /^\.(?:page-header-title|section-title|home-hero-title|card-title|ledger-card-title|text-title|home-featured-title|ledger-row-title|source-card-title|bib-card-title|home-project-inventory-title|home-index-title|article-title|article-related-title|projects-dossier-title)\b/
|
|
277
|
+
|
|
278
|
+
sheets.each do |path|
|
|
279
|
+
relative = Pathname.new(path).relative_path_from(REPO_ROOT)
|
|
280
|
+
cleaned = strip_noise(File.read(path))
|
|
281
|
+
cleaned.scan(/([^{}]+)\{([^{}]*)\}/m) do |sel, body|
|
|
282
|
+
next unless body.match?(/text-transform\s*:\s*uppercase/i)
|
|
283
|
+
|
|
284
|
+
sel.split(",").each do |part|
|
|
285
|
+
name = part.gsub(/\s+/, " ").strip
|
|
286
|
+
next unless name.match?(title_selector_pattern)
|
|
287
|
+
|
|
288
|
+
errors << "#{relative}: title selector #{name} applies text-transform: uppercase (PLT-AC-60)"
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
275
293
|
# PLT-AC-23. The home hero summary uses --color-legend, the ink of the other
|
|
276
294
|
# description texts.
|
|
277
295
|
require_declarations(
|
|
@@ -147,12 +147,12 @@ end
|
|
|
147
147
|
warn "FAIL test-examples-build: #{name} home is missing JSON-LD"
|
|
148
148
|
exit 1
|
|
149
149
|
end
|
|
150
|
-
ledger_faces = "family=
|
|
150
|
+
ledger_faces = "family=Ancizar+Serif:ital,wght@0,400;0,600;1,400"
|
|
151
151
|
unless html.include?(ledger_faces) && html.include?("&display=swap") && html.include?("__pandorgaFontsHref")
|
|
152
152
|
warn "FAIL test-examples-build: #{name} home dropped the ledger font request"
|
|
153
153
|
exit 1
|
|
154
154
|
end
|
|
155
|
-
unless html.include?("--font-title: '
|
|
155
|
+
unless html.include?("--font-title: 'Ancizar Serif', 'Pandorga Ancizar Fallback', serif")
|
|
156
156
|
warn "FAIL test-examples-build: #{name} home dropped the title face"
|
|
157
157
|
exit 1
|
|
158
158
|
end
|
data/scripts/test/test-fonts.rb
CHANGED
|
@@ -16,9 +16,9 @@ end
|
|
|
16
16
|
|
|
17
17
|
require_relative "../../lib/pandorga/fonts"
|
|
18
18
|
|
|
19
|
-
DEFAULT_HREF = "https://fonts.googleapis.com/css2?family=
|
|
19
|
+
DEFAULT_HREF = "https://fonts.googleapis.com/css2?family=Ancizar+Serif:ital,wght@0,400;0,600;1,400" \
|
|
20
20
|
"&family=Marcellus+SC:wght@400;700" \
|
|
21
|
-
"&family=
|
|
21
|
+
"&family=Ancizar+Sans:wght@300;350;400;600" \
|
|
22
22
|
"&family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700" \
|
|
23
23
|
"&display=swap"
|
|
24
24
|
|
|
@@ -27,11 +27,12 @@ bare = Pandorga::Fonts.resolve(nil, nil)
|
|
|
27
27
|
from_theme = Pandorga::Fonts.resolve(nil, theme)
|
|
28
28
|
fail!("unset config dropped the ledger faces") unless Pandorga::Fonts.stylesheet_href(bare) == DEFAULT_HREF
|
|
29
29
|
fail!("theme tokens drifted from the ledger faces") unless Pandorga::Fonts.stylesheet_href(from_theme) == DEFAULT_HREF
|
|
30
|
+
fail!("default stylesheet href contained Cinzel") if DEFAULT_HREF.include?("Cinzel")
|
|
30
31
|
|
|
31
|
-
fail!("title stack dropped
|
|
32
|
-
"'
|
|
33
|
-
fail!("body stack dropped
|
|
34
|
-
"'
|
|
32
|
+
fail!("title stack dropped Ancizar Serif") unless Pandorga::Fonts.css_stack(from_theme, "title") ==
|
|
33
|
+
"'Ancizar Serif', 'Pandorga Ancizar Fallback', serif"
|
|
34
|
+
fail!("body stack dropped Ancizar Sans") unless Pandorga::Fonts.css_stack(from_theme, "body") ==
|
|
35
|
+
"'Ancizar Sans', 'Pandorga Ancizar Sans Fallback', sans-serif"
|
|
35
36
|
fail!("label stack dropped the metric fallback") unless Pandorga::Fonts.css_stack(from_theme, "label") ==
|
|
36
37
|
"'Marcellus SC', 'Pandorga Label Fallback', serif"
|
|
37
38
|
fail!("mono stack dropped Courier Prime") unless Pandorga::Fonts.css_stack(from_theme, "mono").start_with?("'Courier Prime', ui-monospace")
|
|
@@ -43,7 +44,7 @@ site_faces = Pandorga::Fonts.resolve(
|
|
|
43
44
|
fail!("Cinzel stack missed its metric fallback") unless Pandorga::Fonts.css_stack(site_faces, "title") ==
|
|
44
45
|
"'Cinzel', 'Pandorga Cinzel Fallback', serif"
|
|
45
46
|
fail!("Ancizar stack missed its metric fallback") unless Pandorga::Fonts.css_stack(site_faces, "body") ==
|
|
46
|
-
"'Ancizar Serif', 'Pandorga Ancizar Fallback',
|
|
47
|
+
"'Ancizar Serif', 'Pandorga Ancizar Fallback', sans-serif"
|
|
47
48
|
|
|
48
49
|
custom = Pandorga::Fonts.resolve(
|
|
49
50
|
{
|
|
@@ -77,7 +78,7 @@ end
|
|
|
77
78
|
fail!("corrupted 'behind' left by a font rename (#{broken.size} files): #{broken.join(', ')}") unless broken.empty?
|
|
78
79
|
fail!("code face did not follow mono") unless Pandorga::Fonts.css_stack(custom, "code").start_with?("'IBM Plex Mono'")
|
|
79
80
|
|
|
80
|
-
kept = Pandorga::Fonts.resolve({ "title" => "
|
|
81
|
+
kept = Pandorga::Fonts.resolve({ "title" => "Ancizar Serif" }, theme)
|
|
81
82
|
fail!("naming the default title changed the stylesheet") unless Pandorga::Fonts.stylesheet_href(kept) == DEFAULT_HREF
|
|
82
83
|
|
|
83
84
|
ignored = Pandorga::Fonts.resolve({ "title" => "Fraunces</style>" }, theme)
|
|
@@ -104,6 +104,6 @@ fail!("font loader missing") unless font_at && script_at && base_at
|
|
|
104
104
|
fail!("font stylesheet precedes the preload script") unless script_at < font_at
|
|
105
105
|
fail!("font stylesheet precedes local CSS") unless base_at < font_at
|
|
106
106
|
fail!("font host is not preconnected before the preload script") unless preconnect_at && preconnect_at < script_at
|
|
107
|
-
fail!("body fallback is
|
|
107
|
+
fail!("body fallback is not sans-serif") unless shared.include?('body_fallback: "sans-serif"')
|
|
108
108
|
|
|
109
109
|
puts "PASS test-hero-summary"
|
|
@@ -19,8 +19,8 @@ fail!("featured article title is not 2pt larger") unless home.include?(
|
|
|
19
19
|
'[data-home-kind="articles"] .home-featured-title'
|
|
20
20
|
) && home.include?("calc(var(--home-featured-title-size) + 2pt)")
|
|
21
21
|
|
|
22
|
-
fail!("listed article titles are not
|
|
23
|
-
"
|
|
22
|
+
fail!("listed article titles are not on --type-card step") unless home.include?(
|
|
23
|
+
"font-size: var(--type-card);"
|
|
24
24
|
) && home.include?('[data-home-kind="articles"] .home-card-grid .card-title')
|
|
25
25
|
|
|
26
26
|
phone = home[/^@media \(max-width: 767px\) \{\n \[data-home-kind="articles"\] \.home-featured \{.*?\n \}/m]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Acceptance tests for Mobile density + home measure (pandorga theme UI).
|
|
5
|
+
#
|
|
6
|
+
# 1. Article page top on mobile: meta date row and action buttons share one band,
|
|
7
|
+
# and TOC disclosure / reading column gaps are bounded.
|
|
8
|
+
# 2. Fixed top bar on mobile: shorter vertical padding (≤56-60px height) and
|
|
9
|
+
# hide-on-scroll functionality.
|
|
10
|
+
# 3. Home hero summary measure on desktop: constrained to ~60-70ch (e.g. 65ch).
|
|
11
|
+
# 4. Home section "View all" next to title: grid-template-columns places CTA
|
|
12
|
+
# beside section title, not at far right of wide band.
|
|
13
|
+
|
|
14
|
+
require "pathname"
|
|
15
|
+
|
|
16
|
+
ROOT = Pathname.new(__dir__).join("../..").expand_path
|
|
17
|
+
CSS = ROOT.join("assets/css")
|
|
18
|
+
|
|
19
|
+
$failures = []
|
|
20
|
+
|
|
21
|
+
def fail!(msg)
|
|
22
|
+
$failures << "FAIL test-mobile-density-home-measure: #{msg}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def strip_comments(text)
|
|
26
|
+
text.gsub(%r{/\*.*?\*/}m, "")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def parse_css_rules(css_text)
|
|
30
|
+
rules = []
|
|
31
|
+
i = 0
|
|
32
|
+
head = +""
|
|
33
|
+
while i < css_text.length
|
|
34
|
+
c = css_text[i]
|
|
35
|
+
if c == "{"
|
|
36
|
+
depth = 1
|
|
37
|
+
j = i + 1
|
|
38
|
+
while j < css_text.length && depth.positive?
|
|
39
|
+
depth += 1 if css_text[j] == "{"
|
|
40
|
+
depth -= 1 if css_text[j] == "}"
|
|
41
|
+
j += 1
|
|
42
|
+
end
|
|
43
|
+
body = css_text[(i + 1)...(j - 1)]
|
|
44
|
+
h = head.strip
|
|
45
|
+
if h.start_with?("@media")
|
|
46
|
+
media_cond = h.sub(/\A@media\s*/, "").gsub(/\s+/, "")
|
|
47
|
+
sub_rules = parse_css_rules(body)
|
|
48
|
+
sub_rules.each do |r|
|
|
49
|
+
m = r[:media] ? "#{media_cond} and #{r[:media]}" : media_cond
|
|
50
|
+
rules << r.merge(media: m)
|
|
51
|
+
end
|
|
52
|
+
elsif h.start_with?("@supports", "@layer", "@container")
|
|
53
|
+
rules.concat(parse_css_rules(body))
|
|
54
|
+
elsif !h.start_with?("@")
|
|
55
|
+
decls = body.split(";").filter_map do |d|
|
|
56
|
+
prop, val = d.split(":", 2)
|
|
57
|
+
next unless val
|
|
58
|
+
|
|
59
|
+
[prop.strip.downcase, val.gsub(/\s+/, " ").strip]
|
|
60
|
+
end
|
|
61
|
+
h.split(",").each do |sel|
|
|
62
|
+
s = sel.gsub(/\s+/, " ").strip
|
|
63
|
+
rules << { sel: s, decls: decls, media: nil }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
head = +""
|
|
67
|
+
i = j
|
|
68
|
+
elsif c == "}"
|
|
69
|
+
i += 1
|
|
70
|
+
else
|
|
71
|
+
head << c
|
|
72
|
+
i += 1
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
rules
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# 1. Article page top on mobile
|
|
79
|
+
detail_css = strip_comments(File.read(CSS.join("ledger/detail.css")))
|
|
80
|
+
detail_rules = parse_css_rules(detail_css)
|
|
81
|
+
|
|
82
|
+
meta_mobile_rules = detail_rules.select do |r|
|
|
83
|
+
r[:media] && r[:media].include?("max-width:767px") && r[:sel] == ".text-detail-sidebar .article-meta"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
has_row_layout = meta_mobile_rules.any? do |r|
|
|
87
|
+
r[:decls].any? { |p, v| p == "flex-direction" && v == "row" }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
unless has_row_layout
|
|
91
|
+
fail!("detail.css must set flex-direction: row on .text-detail-sidebar .article-meta under @media (max-width: 767px)")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
toc_disclosure_mobile = detail_rules.select do |r|
|
|
95
|
+
r[:media] && r[:media].include?("max-width:767px") && r[:sel].include?("article-toc--disclosure")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
has_bounded_toc_margin = toc_disclosure_mobile.any? do |r|
|
|
99
|
+
r[:decls].any? { |p, v| (p == "margin-top" || p == "margin-bottom" || p == "margin") && (v.include?("var(--space-xs)") || v.include?("var(--space-sm)") || v.include?("0")) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
unless has_bounded_toc_margin
|
|
103
|
+
fail!("detail.css must bound margins on .article-toc--disclosure under @media (max-width: 767px)")
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# 2. Fixed top bar on mobile
|
|
107
|
+
chrome_css = strip_comments(File.read(CSS.join("ledger/chrome.css")))
|
|
108
|
+
chrome_rules = parse_css_rules(chrome_css)
|
|
109
|
+
|
|
110
|
+
topbar_mobile_padding = chrome_rules.any? do |r|
|
|
111
|
+
r[:media] && r[:media].include?("max-width:767px") && (r[:sel] == ".site-topbar > div" || r[:sel] == ".site-topbar") &&
|
|
112
|
+
r[:decls].any? { |p, v| (p == "padding-top" || p == "padding-bottom" || p == "padding" || p == "padding-block") && (v.include?("0.625rem") || v.include?("0.5rem") || v.include?("0.75rem") || v.include?("10px") || v.include?("12px")) }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
unless topbar_mobile_padding
|
|
116
|
+
fail!("chrome.css must reduce vertical padding on .site-topbar under @media (max-width: 767px)")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
topbar_hide_rule = chrome_rules.any? do |r|
|
|
120
|
+
(r[:sel].include?(".site-topbar.topbar--hidden") || r[:sel].include?(".site-topbar[data-topbar-hidden]")) &&
|
|
121
|
+
r[:decls].any? { |p, v| p == "transform" && v.include?("translateY(-100%)") }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
unless topbar_hide_rule
|
|
125
|
+
fail!("chrome.css must define transform: translateY(-100%) on .site-topbar.topbar--hidden")
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
shell_html = File.read(ROOT.join("_layouts/shell.html"))
|
|
129
|
+
unless shell_html.include?("topbar--hidden") || shell_html.include?("data-topbar-hidden")
|
|
130
|
+
fail!("_layouts/shell.html must contain hide-on-scroll logic for .site-topbar")
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# 3. Home hero summary measure on desktop
|
|
134
|
+
home_css = strip_comments(File.read(CSS.join("ledger/home.css")))
|
|
135
|
+
home_rules = parse_css_rules(home_css)
|
|
136
|
+
|
|
137
|
+
hero_summary_desktop = home_rules.any? do |r|
|
|
138
|
+
r[:media] && r[:media].include?("min-width:768px") && r[:sel] == ".home-hero-summary" &&
|
|
139
|
+
r[:decls].any? { |p, v| p == "max-width" && (v == "65ch" || v == "60ch" || v == "70ch") }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
unless hero_summary_desktop
|
|
143
|
+
fail!("home.css must constrain .home-hero-summary max-width to ~65ch under @media (min-width: 768px)")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# 4. Home section "View all" next to title
|
|
147
|
+
banner_grid_col = home_rules.any? do |r|
|
|
148
|
+
r[:sel] == ".home-band:not(.home-band--hero) .ledger-section-banner" &&
|
|
149
|
+
r[:decls].any? { |p, v| p == "grid-template-columns" && (v.start_with?("auto auto") || v.start_with?("minmax(0, auto) auto") || v.start_with?("max-content max-content")) }
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
unless banner_grid_col
|
|
153
|
+
fail!("home.css must set grid-template-columns on .ledger-section-banner to place CTA beside title (e.g. auto auto 1fr)")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if $failures.empty?
|
|
157
|
+
puts "PASS test-mobile-density-home-measure"
|
|
158
|
+
else
|
|
159
|
+
$failures.each { |f| puts f }
|
|
160
|
+
exit 1
|
|
161
|
+
end
|
|
@@ -290,7 +290,7 @@ end
|
|
|
290
290
|
item(5) do
|
|
291
291
|
h = "ledger/home.css"
|
|
292
292
|
expect(h, ".home-band:not(.home-band--hero) .ledger-section-banner", {
|
|
293
|
-
"display" => "grid", "grid-template-columns" => "
|
|
293
|
+
"display" => "grid", "grid-template-columns" => "auto auto 1fr"
|
|
294
294
|
})
|
|
295
295
|
expect(h, ".home-band .ledger-section-lead", { "display" => "contents" })
|
|
296
296
|
expect(h, ".home-band .ledger-section-banner .ledger-kicker", { "grid-column" => "1 / -1", "grid-row" => "1" })
|
data/scripts/validate.sh
CHANGED
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: 2.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The Pandorga contributors
|
|
@@ -453,6 +453,7 @@ files:
|
|
|
453
453
|
- scripts/test/test-listing-filter-contract.rb
|
|
454
454
|
- scripts/test/test-math-row-breaks.mjs
|
|
455
455
|
- scripts/test/test-math-row-breaks.rb
|
|
456
|
+
- scripts/test/test-mobile-density-home-measure.rb
|
|
456
457
|
- scripts/test/test-new.rb
|
|
457
458
|
- scripts/test/test-no-personal-data.rb
|
|
458
459
|
- scripts/test/test-parallel-content-publish.rb
|
|
@@ -570,7 +571,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
570
571
|
requirements:
|
|
571
572
|
- - ">="
|
|
572
573
|
- !ruby/object:Gem::Version
|
|
573
|
-
version: 3.
|
|
574
|
+
version: 3.2.0
|
|
574
575
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
575
576
|
requirements:
|
|
576
577
|
- - ">="
|