@canopy-iiif/app 0.8.6 → 0.9.1

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.
@@ -11,7 +11,7 @@
11
11
  --color-brand-600: #3358d4;
12
12
  --color-brand-700: #3a5bc7;
13
13
  --color-brand-800: #1f2d5c;
14
- --color-brand-900: #1a264e;
14
+ --color-brand-900: #172245;
15
15
  --color-brand-default: #3358d4;
16
16
  --color-gray-50: #fcfcfd;
17
17
  --color-gray-100: #f0f0f3;
@@ -22,15 +22,15 @@
22
22
  --color-gray-600: #80838d;
23
23
  --color-gray-700: #60646c;
24
24
  --color-gray-800: #1c2024;
25
- --color-gray-900: #181b1f;
26
- --color-gray-default: #181b1f;
25
+ --color-gray-900: #111316;
26
+ --color-gray-default: #111316;
27
27
  --color-gray-muted: #80838d;
28
28
  --colors-accent: #3358d4 !important;
29
29
  --colors-accentAlt: #1f2d5c !important;
30
30
  --colors-accentMuted: #8da4ef !important;
31
- --colors-primary: #181b1f !important;
32
- --colors-primaryAlt: #181b1f !important;
33
- --colors-primaryMuted: #181b1f !important;
31
+ --colors-primary: #111316 !important;
32
+ --colors-primaryAlt: #111316 !important;
33
+ --colors-primaryMuted: #111316 !important;
34
34
  --colors-secondary: #fcfcfd !important;
35
35
  --colors-secondaryAlt: #fcfcfd !important;
36
36
  --colors-secondaryMuted: #fcfcfd !important;
@@ -47,7 +47,7 @@
47
47
  --color-brand-600: #3358d4;
48
48
  --color-brand-700: #3a5bc7;
49
49
  --color-brand-800: #1f2d5c;
50
- --color-brand-900: #1a264e;
50
+ --color-brand-900: #172245;
51
51
  --color-brand-default: #3358d4;
52
52
  --color-gray-50: #fcfcfd;
53
53
  --color-gray-100: #f0f0f3;
@@ -58,15 +58,15 @@
58
58
  --color-gray-600: #80838d;
59
59
  --color-gray-700: #60646c;
60
60
  --color-gray-800: #1c2024;
61
- --color-gray-900: #181b1f;
62
- --color-gray-default: #181b1f;
61
+ --color-gray-900: #111316;
62
+ --color-gray-default: #111316;
63
63
  --color-gray-muted: #80838d;
64
64
  --colors-accent: #3358d4 !important;
65
65
  --colors-accentAlt: #1f2d5c !important;
66
66
  --colors-accentMuted: #8da4ef !important;
67
- --colors-primary: #181b1f !important;
68
- --colors-primaryAlt: #181b1f !important;
69
- --colors-primaryMuted: #181b1f !important;
67
+ --colors-primary: #111316 !important;
68
+ --colors-primaryAlt: #111316 !important;
69
+ --colors-primaryMuted: #111316 !important;
70
70
  --colors-secondary: #fcfcfd !important;
71
71
  --colors-secondaryAlt: #fcfcfd !important;
72
72
  --colors-secondaryMuted: #fcfcfd !important;
@@ -130,6 +130,38 @@ h2 {
130
130
  font-weight: 400;
131
131
  }
132
132
 
133
+ .canopy-button {
134
+ display: inline-flex;
135
+ align-items: center;
136
+ justify-content: center;
137
+ gap: 0.35rem;
138
+ padding: 0.55rem 1.4rem;
139
+ border-radius: 9999px;
140
+ font-size: 0.9375rem;
141
+ font-weight: 600;
142
+ text-decoration: none;
143
+ transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
144
+ }
145
+ .canopy-button--primary {
146
+ background-color: var(--color-brand-600, #4f46e5);
147
+ color: var(--color-gray-50, #f9fafb);
148
+ box-shadow: 0 18px 32px -22px rgba(15, 23, 42, 0.55);
149
+ }
150
+ .canopy-button--primary:hover, .canopy-button--primary:focus-visible {
151
+ background-color: var(--color-brand-700, #4338ca);
152
+ color: var(--color-gray-50, #f9fafb);
153
+ }
154
+ .canopy-button--secondary {
155
+ border: 1px solid color-mix(in srgb, var(--color-gray-400, #94a3b8) 60%, transparent);
156
+ color: color-mix(in srgb, var(--color-gray-900, #0f172a) 92%, transparent);
157
+ background-color: transparent;
158
+ }
159
+ .canopy-button--secondary:hover, .canopy-button--secondary:focus-visible {
160
+ border-color: color-mix(in srgb, var(--color-brand-400, #818cf8) 65%, transparent);
161
+ background-color: color-mix(in srgb, var(--color-brand-200, #c7d2fe) 25%, transparent);
162
+ color: var(--color-brand-700, #4338ca);
163
+ }
164
+
133
165
  /* Canopy Card component styles */
134
166
  .canopy-card, .canopy-annotation-card {
135
167
  display: block;
@@ -280,27 +312,337 @@ html.dark a.canopy-logo svg path.canopy-logo-overlay {
280
312
  text-decoration: underline;
281
313
  }
282
314
 
283
- .canopy-hero {
315
+ .canopy-interstitial {
316
+ position: relative;
317
+ width: 100%;
318
+ isolation: isolate;
319
+ margin-bottom: 2.618rem;
320
+ }
321
+ .canopy-interstitial--hero {
322
+ --hero-height: 520px;
323
+ --hero-bg-start: var(--color-brand-50);
324
+ --hero-bg-end: var(--color-brand-200);
325
+ --hero-caption-link: var(--color-brand-700);
326
+ --hero-dot-bg: var(--color-brand-300);
327
+ --hero-dot-active-bg: var(--color-brand-700);
328
+ background: linear-gradient(135deg, var(--hero-bg-start) 0%, var(--hero-bg-end) 100%);
329
+ min-height: var(--hero-height);
330
+ }
331
+ .canopy-interstitial--hero .canopy-interstitial__overlay,
332
+ .canopy-interstitial--hero .canopy-interstitial__pane::before,
333
+ .canopy-interstitial--hero .canopy-interstitial__pane::after,
334
+ .canopy-interstitial--hero .canopy-interstitial__veil {
335
+ display: none;
336
+ }
337
+ .canopy-interstitial--hero .canopy-interstitial__layout {
338
+ display: flex;
339
+ flex-direction: column;
340
+ min-height: var(--hero-height);
341
+ width: 100%;
342
+ overflow: hidden;
343
+ }
344
+ .canopy-interstitial--hero .canopy-interstitial__panel {
345
+ flex: 1;
346
+ display: flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ padding: clamp(2.5rem, 5vw, 4rem) clamp(2rem, 4vw, 3.5rem);
350
+ box-sizing: border-box;
351
+ min-width: 0;
352
+ background: transparent;
353
+ }
354
+ .canopy-interstitial--hero .canopy-interstitial__body {
355
+ align-items: flex-start;
356
+ text-align: left;
357
+ width: 100%;
358
+ max-width: none;
359
+ margin: 0;
360
+ gap: 1.5rem;
361
+ }
362
+ .canopy-interstitial--hero .canopy-interstitial__actions {
363
+ justify-content: flex-start;
364
+ }
365
+ .canopy-interstitial--hero .canopy-interstitial__media-group {
366
+ flex: 1;
367
+ position: relative;
368
+ min-height: var(--hero-height);
369
+ display: flex;
370
+ align-items: center;
371
+ justify-content: center;
372
+ padding: clamp(1.5rem, 4vw, 3rem);
373
+ box-sizing: border-box;
374
+ min-width: 0;
375
+ overflow: hidden;
376
+ background: transparent;
377
+ }
378
+ .canopy-interstitial--hero .canopy-interstitial__caption {
379
+ position: static;
380
+ margin: 1.5rem auto 0;
381
+ padding: 0 0.5rem;
382
+ text-align: center;
383
+ }
384
+ .canopy-interstitial--hero .canopy-interstitial__panel,
385
+ .canopy-interstitial--hero .canopy-interstitial__media-group {
386
+ width: 50%;
387
+ }
388
+ .canopy-interstitial--hero .canopy-interstitial__pagination {
389
+ position: static;
390
+ transform: none;
391
+ justify-content: center;
392
+ margin-top: 1.618rem;
393
+ padding-bottom: 2px;
394
+ }
395
+ @media (max-width: 63.9375rem) {
396
+ .canopy-interstitial--hero .canopy-interstitial__panel,
397
+ .canopy-interstitial--hero .canopy-interstitial__media-group {
398
+ width: 100%;
399
+ }
400
+ }
401
+ @media (min-width: 48rem) {
402
+ .canopy-interstitial--hero .canopy-interstitial__layout {
403
+ flex-direction: row;
404
+ }
405
+ }
406
+ @media (min-width: 64rem) {
407
+ .canopy-interstitial--hero .canopy-interstitial__body .canopy-interstitial__headline {
408
+ font-size: 3rem;
409
+ }
410
+ }
411
+ .canopy-interstitial__slider {
412
+ position: relative;
413
+ width: 100%;
414
+ min-height: var(--hero-height);
415
+ overflow: hidden;
416
+ }
417
+ .canopy-interstitial__slider .swiper-wrapper {
418
+ display: flex;
419
+ transition-property: transform;
420
+ }
421
+ .canopy-interstitial__slider .swiper-slide {
422
+ width: 100%;
423
+ flex-shrink: 0;
424
+ display: flex;
425
+ justify-content: center;
426
+ align-items: stretch;
427
+ }
428
+ .canopy-interstitial__pane {
429
+ position: relative;
430
+ width: 100%;
431
+ min-height: var(--hero-height);
432
+ overflow: hidden;
433
+ background-color: color-mix(in srgb, var(--hero-bg-start) 65%, transparent);
434
+ }
435
+ .canopy-interstitial__pane::before, .canopy-interstitial__pane::after {
436
+ content: "";
437
+ position: absolute;
438
+ inset: 0;
439
+ pointer-events: none;
440
+ z-index: 1;
441
+ display: none;
442
+ }
443
+ .canopy-interstitial__pane--flat::before, .canopy-interstitial__pane--flat::after {
444
+ display: none;
445
+ }
446
+ .canopy-interstitial__pane--static {
447
+ display: flex;
448
+ flex-direction: column;
449
+ height: var(--hero-height);
450
+ background-color: transparent;
451
+ }
452
+ .canopy-interstitial__media {
453
+ position: absolute;
454
+ inset: 0;
455
+ width: 100%;
456
+ height: 100%;
457
+ object-fit: cover;
458
+ object-position: center;
459
+ z-index: 0;
460
+ }
461
+ .canopy-interstitial__media-frame {
462
+ position: relative;
463
+ width: 100%;
464
+ height: 100%;
465
+ flex: 1 1 auto;
466
+ overflow: hidden;
467
+ border-radius: clamp(1.5rem, 2vw, 2rem);
468
+ }
469
+ .canopy-interstitial__media--static {
470
+ position: static;
471
+ display: block;
472
+ width: 100%;
473
+ height: 100%;
474
+ object-fit: cover;
475
+ object-position: center;
476
+ }
477
+ .canopy-interstitial__veil {
478
+ position: absolute;
479
+ inset: 0;
480
+ pointer-events: none;
481
+ z-index: 2;
482
+ display: none;
483
+ }
484
+ .canopy-interstitial__overlay {
485
+ pointer-events: none;
486
+ position: absolute;
487
+ inset: 0;
488
+ z-index: 6;
489
+ display: flex;
490
+ align-items: center;
491
+ justify-content: center;
492
+ padding: 4rem 1.75rem;
493
+ background: linear-gradient(135deg, color-mix(in srgb, var(--hero-bg-start) 85%, transparent) 0%, color-mix(in srgb, var(--hero-bg-end) 70%, transparent) 100%);
494
+ }
495
+ .canopy-interstitial__body {
496
+ position: relative;
497
+ z-index: 3;
498
+ max-width: 48rem;
499
+ margin: 0 auto;
284
500
  padding: 0;
501
+ display: flex;
502
+ flex-direction: column;
503
+ align-items: center;
504
+ gap: 1.5rem;
505
+ text-align: center;
506
+ pointer-events: auto;
507
+ }
508
+ .canopy-interstitial__headline {
285
509
  margin: 0;
510
+ font-size: 2rem;
511
+ font-weight: 600;
512
+ line-height: 1.08;
513
+ letter-spacing: -0.01em;
286
514
  }
287
- .canopy-hero figcaption {
515
+ .canopy-interstitial__description {
288
516
  margin: 0;
289
- padding: 1rem 0 0;
517
+ max-width: 42rem;
290
518
  font-size: 1rem;
291
- text-align: right;
519
+ line-height: 1.7;
520
+ color: color-mix(in srgb, currentColor 82%, transparent);
521
+ }
522
+ .canopy-interstitial__actions {
523
+ display: flex;
524
+ flex-wrap: wrap;
525
+ justify-content: center;
526
+ gap: 0.75rem;
527
+ }
528
+ .canopy-interstitial__caption {
529
+ position: absolute;
530
+ bottom: 1.75rem;
531
+ right: 2rem;
532
+ z-index: 3;
533
+ font-size: 0.8333rem;
534
+ letter-spacing: 0.12em;
535
+ text-transform: uppercase;
536
+ color: var(--hero-caption-color);
537
+ }
538
+ .canopy-interstitial__caption--static {
539
+ position: static;
540
+ margin: 1.5rem auto 0;
541
+ text-align: center;
542
+ color: var(--hero-caption-color);
543
+ }
544
+ .canopy-interstitial__caption-link {
545
+ color: var(--hero-caption-link);
546
+ text-decoration: underline;
547
+ text-underline-offset: 0.35em;
548
+ text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
549
+ transition: color 0.2s ease, text-decoration-color 0.2s ease;
550
+ }
551
+ .canopy-interstitial__caption-link:hover, .canopy-interstitial__caption-link:focus-visible {
552
+ color: var(--hero-caption-link);
553
+ text-decoration-color: color-mix(in srgb, currentColor 55%, transparent);
554
+ }
555
+ .canopy-interstitial__nav {
556
+ position: absolute;
557
+ inset: 0;
558
+ z-index: 4;
559
+ }
560
+ .canopy-interstitial__nav-btn {
561
+ position: absolute;
562
+ top: 50%;
563
+ transform: translateY(-50%);
564
+ width: 2.75rem;
565
+ height: 2.75rem;
566
+ border-radius: 9999px;
567
+ border: 1px solid color-mix(in srgb, var(--color-gray-400, #94a3b8) 55%, transparent);
568
+ background-color: color-mix(in srgb, var(--color-gray-100, #f1f5f9) 65%, transparent);
569
+ backdrop-filter: blur(6px);
570
+ cursor: pointer;
571
+ display: inline-flex;
572
+ align-items: center;
573
+ justify-content: center;
574
+ transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
575
+ color: var(--colors-primary);
576
+ }
577
+ .canopy-interstitial__nav-btn::after {
578
+ content: "";
579
+ border: solid currentColor;
580
+ border-width: 0 2px 2px 0;
581
+ display: inline-block;
582
+ padding: 7px;
583
+ transform: rotate(135deg);
584
+ }
585
+ .canopy-interstitial__nav-btn--prev {
586
+ left: 1.5rem;
587
+ }
588
+ .canopy-interstitial__nav-btn--next {
589
+ right: 1.5rem;
590
+ }
591
+ .canopy-interstitial__nav-btn--next::after {
592
+ transform: rotate(-45deg);
593
+ }
594
+ .canopy-interstitial__nav-btn:hover, .canopy-interstitial__nav-btn:focus-visible {
595
+ background-color: color-mix(in srgb, var(--color-brand-200) 55%, transparent);
596
+ border-color: color-mix(in srgb, var(--color-brand-400, #818cf8) 70%, transparent);
597
+ color: var(--color-brand-700, #4338ca);
598
+ }
599
+ .canopy-interstitial__pagination {
600
+ position: absolute;
601
+ z-index: 4;
602
+ bottom: 1rem;
603
+ left: 50%;
604
+ transform: translateX(-50%);
605
+ display: flex;
606
+ gap: 0.4rem;
607
+ }
608
+ .canopy-interstitial__pagination .swiper-pagination-bullet {
609
+ display: inline-block;
610
+ width: 0.45rem;
611
+ height: 0.45rem;
612
+ border-radius: 9999px;
613
+ background-color: var(--hero-dot-bg);
614
+ opacity: 1;
615
+ transition: transform 0.2s ease, background-color 0.2s ease;
616
+ }
617
+ .canopy-interstitial__pagination .swiper-pagination-bullet-active {
618
+ transform: scale(1.35);
619
+ background-color: var(--hero-dot-active-bg);
292
620
  }
293
621
 
294
- .canopy-hero-link {
295
- display: block;
296
- color: inherit;
297
- text-decoration: none;
622
+ .canopy-interstitial--hero.canopy-interstitial--bg-transparent {
623
+ background: none;
298
624
  }
299
625
 
300
- .canopy-hero__media {
301
- position: relative;
626
+ .canopy-interstitial--hero.canopy-interstitial--bg-transparent .canopy-interstitial__overlay {
627
+ background: none;
302
628
  }
303
629
 
630
+ @media (min-width: 40rem) {
631
+ .canopy-interstitial__body {
632
+ padding-inline: 2.75rem;
633
+ }
634
+ .canopy-interstitial__headline {
635
+ font-size: 3rem;
636
+ }
637
+ .canopy-interstitial__description {
638
+ font-size: 1.0625rem;
639
+ }
640
+ }
641
+ @media (min-width: 64rem) {
642
+ .canopy-interstitial__body {
643
+ padding-inline: 4rem;
644
+ }
645
+ }
304
646
  .canopy-search-form-shell {
305
647
  --search-form-label-padding-x: 0.75rem;
306
648
  --search-form-label-padding-y: 0.625rem;
@@ -847,7 +1189,6 @@ html.dark a.canopy-logo svg path.canopy-logo-overlay {
847
1189
  }
848
1190
 
849
1191
  .canopy-sub-navigation {
850
- font-size: 0.95rem;
851
1192
  color: inherit;
852
1193
  }
853
1194
 
package/ui/theme.js CHANGED
@@ -36,7 +36,10 @@ function loadSassVariableTokens() {
36
36
  }
37
37
  return {map: vars, css};
38
38
  } catch (error) {
39
- debugLog("failed to compile Sass variables", error && error.message ? error.message : error);
39
+ debugLog(
40
+ "failed to compile Sass variables",
41
+ error && error.message ? error.message : error
42
+ );
40
43
  return {map: {}, css: ""};
41
44
  }
42
45
  }
@@ -126,6 +129,26 @@ function darkenHex(hex, amount = 0.15) {
126
129
  return `#${toHex(r * factor)}${toHex(g * factor)}${toHex(b * factor)}`;
127
130
  }
128
131
 
132
+ function lightenHex(hex, amount = 0.15) {
133
+ if (!hex) return hex;
134
+ const normalized = hex.replace("#", "");
135
+ if (!/^[0-9a-fA-F]{6}$/.test(normalized)) return hex;
136
+ const num = parseInt(normalized, 16);
137
+ const r = (num >> 16) & 255;
138
+ const g = (num >> 8) & 255;
139
+ const b = num & 255;
140
+ const clamp = (value) => Math.max(0, Math.min(255, Math.round(value)));
141
+ const toHex = (value) => clamp(value).toString(16).padStart(2, "0");
142
+ const adjust = (value) => value + (255 - value) * amount;
143
+ return `#${toHex(adjust(r))}${toHex(adjust(g))}${toHex(adjust(b))}`;
144
+ }
145
+
146
+ function normalizeDarkenAmount(raw) {
147
+ const value = Number(raw);
148
+ if (!Number.isFinite(value)) return null;
149
+ return Math.min(0.95, Math.max(0, value));
150
+ }
151
+
129
152
  function toTailwindScale(name, options = {}) {
130
153
  if (!name || !AVAILABLE.has(name)) return null;
131
154
  const appearance = normalizeAppearance(options.appearance);
@@ -133,6 +156,7 @@ function toTailwindScale(name, options = {}) {
133
156
  if (!palette) return null;
134
157
  const prefix = name;
135
158
  const scale = {};
159
+ const darken900Amount = normalizeDarkenAmount(options.darken900Amount);
136
160
  for (const lvl of LEVELS) {
137
161
  const radixStep = STEP_MAP[lvl];
138
162
  const key = `${prefix}${radixStep}`;
@@ -142,7 +166,11 @@ function toTailwindScale(name, options = {}) {
142
166
  }
143
167
  const darkestKey = `${prefix}${STEP_MAP["900"]}`;
144
168
  if (scale["800"] && palette[darkestKey]) {
145
- scale["900"] = darkenHex(palette[darkestKey], 0.15);
169
+ const amount = darken900Amount != null ? darken900Amount : 0.25;
170
+ scale["900"] =
171
+ appearance === "dark"
172
+ ? lightenHex(palette[darkestKey], amount)
173
+ : darkenHex(palette[darkestKey], amount);
146
174
  }
147
175
  return scale;
148
176
  }
@@ -226,17 +254,25 @@ function loadCanopyTheme(options = {}) {
226
254
  }
227
255
 
228
256
  let grayName = normalizePaletteName(grayRequested);
229
- let grayScale = grayName ? toTailwindScale(grayName, {appearance}) : null;
257
+ let grayScale = grayName
258
+ ? toTailwindScale(grayName, {appearance, darken900Amount: 0.4})
259
+ : null;
230
260
  let grayFallback = false;
231
261
  if (!grayScale) {
232
262
  grayFallback = true;
233
263
  grayName = DEFAULT_GRAY;
234
- grayScale = toTailwindScale(DEFAULT_GRAY, {appearance});
264
+ grayScale = toTailwindScale(DEFAULT_GRAY, {
265
+ appearance,
266
+ darken900Amount: 0.4,
267
+ });
235
268
  }
236
269
 
237
270
  const sassTokens = loadSassVariableTokens();
238
271
  const dynamicVars = buildVariablesMap(accentScale, grayScale, {appearance});
239
- const mergedVars = {...(sassTokens && sassTokens.map ? sassTokens.map : {}), ...dynamicVars};
272
+ const mergedVars = {
273
+ ...(sassTokens && sassTokens.map ? sassTokens.map : {}),
274
+ ...dynamicVars,
275
+ };
240
276
  const css = variablesToCss(mergedVars);
241
277
  const sassConfig = buildSassConfig(accentScale, grayScale);
242
278
 
@@ -1,60 +0,0 @@
1
- const { fs, path, OUT_DIR } = require('../common');
2
- const { logLine } = require('./log');
3
-
4
- function readFileSafe(p) {
5
- try { return fs.readFileSync(p, 'utf8'); } catch (_) { return ''; }
6
- }
7
-
8
- function hasHtmlFiles(dir) {
9
- let count = 0;
10
- if (!fs.existsSync(dir)) return 0;
11
- const entries = fs.readdirSync(dir, { withFileTypes: true });
12
- for (const e of entries) {
13
- const p = path.join(dir, e.name);
14
- if (e.isDirectory()) count += hasHtmlFiles(p);
15
- else if (e.isFile() && p.toLowerCase().endsWith('.html')) count++;
16
- }
17
- return count;
18
- }
19
-
20
- function verifyHomepageElements(outDir) {
21
- const idx = path.join(outDir, 'index.html');
22
- const html = readFileSafe(idx);
23
- const okHero = /class=\"[^\"]*canopy-hero/.test(html) || /<div[^>]+canopy-hero/.test(html);
24
- const okSearchForm = /data-canopy-search-form=/.test(html);
25
- const okSearchFormTrigger = /data-canopy-search-form-trigger/.test(html);
26
- const okSearchFormScriptRef = /<script[^>]+canopy-search-form\.js/.test(html);
27
- return { okHero, okSearchForm, okSearchFormTrigger, okSearchFormScriptRef, htmlPath: idx };
28
- }
29
-
30
- function verifyBuildOutput(options = {}) {
31
- const outDir = path.resolve(options.outDir || OUT_DIR);
32
- logLine("\nVerify build output", "magenta", { bright: true, underscore: true });
33
- const total = hasHtmlFiles(outDir);
34
- const okAny = total > 0;
35
- const indexPath = path.join(outDir, 'index.html');
36
- const hasIndex = fs.existsSync(indexPath) && fs.statSync(indexPath).size > 0;
37
- const { okHero, okSearchForm, okSearchFormTrigger, okSearchFormScriptRef } = verifyHomepageElements(outDir);
38
-
39
- const ck = (label, ok, extra) => {
40
- const status = ok ? '✓' : '✗';
41
- logLine(`${status} ${label}${extra ? ` ${extra}` : ''}`, ok ? 'green' : 'red');
42
- };
43
-
44
- ck('HTML pages exist', okAny, okAny ? `(${total})` : '');
45
- ck('homepage exists', hasIndex, hasIndex ? `(${indexPath})` : '');
46
- ck('homepage: Hero present', okHero);
47
- ck('homepage: Search form present', okSearchForm);
48
- ck('homepage: Search form trigger present', okSearchFormTrigger);
49
- ck('homepage: Search form script referenced', okSearchFormScriptRef);
50
-
51
- // Do not fail build on missing SSR trigger; the client runtime injects a default.
52
- const ok = okAny && hasIndex && okHero && okSearchForm && okSearchFormScriptRef;
53
- if (!ok) {
54
- const err = new Error('Build verification failed');
55
- err.outDir = outDir;
56
- throw err;
57
- }
58
- }
59
-
60
- module.exports = { verifyBuildOutput };
@@ -1,21 +0,0 @@
1
- .canopy-hero {
2
- padding: 0;
3
- margin: 0;
4
-
5
- figcaption {
6
- margin: 0;
7
- padding: 1rem 0 0;
8
- font-size: 1rem;
9
- text-align: right;
10
- }
11
- }
12
-
13
- .canopy-hero-link {
14
- display: block;
15
- color: inherit;
16
- text-decoration: none;
17
- }
18
-
19
- .canopy-hero__media {
20
- position: relative;
21
- }