ferst-core 0.2.8 → 0.2.9
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.
|
@@ -22,8 +22,9 @@ interface Props {
|
|
|
22
22
|
overlay?: number;
|
|
23
23
|
align?: 'start' | 'center' | 'end';
|
|
24
24
|
parallax?: boolean;
|
|
25
|
+
fullBleed?: boolean;
|
|
25
26
|
}
|
|
26
|
-
const { image, eyebrow, title, text, cta, overlay = 50, align = 'start', parallax = true } = Astro.props;
|
|
27
|
+
const { image, eyebrow, title, text, cta, overlay = 50, align = 'start', parallax = true, fullBleed = false } = Astro.props;
|
|
27
28
|
const scrim = overlay / 100;
|
|
28
29
|
---
|
|
29
30
|
|
|
@@ -31,6 +32,7 @@ const scrim = overlay / 100;
|
|
|
31
32
|
class="b-banner"
|
|
32
33
|
data-align={align}
|
|
33
34
|
data-parallax={parallax ? 'true' : 'false'}
|
|
35
|
+
data-full-bleed={fullBleed ? 'true' : 'false'}
|
|
34
36
|
style={`--banner-image: url('${image}'); --banner-scrim: ${scrim};`}
|
|
35
37
|
>
|
|
36
38
|
<div class="b-banner__scrim" aria-hidden="true"></div>
|
|
@@ -60,6 +62,16 @@ const scrim = overlay / 100;
|
|
|
60
62
|
background-position: center;
|
|
61
63
|
background-repeat: no-repeat;
|
|
62
64
|
}
|
|
65
|
+
/* Full-bleed: break out of the content column to the full viewport width, edges
|
|
66
|
+
squared off. Safe from a horizontal scrollbar because Base sets overflow-x:
|
|
67
|
+
clip on html/body. The card keeps a comfortable inset that tracks the content
|
|
68
|
+
column on wide screens, so it still lines up with the page below. */
|
|
69
|
+
.b-banner[data-full-bleed='true'] {
|
|
70
|
+
width: 100vw;
|
|
71
|
+
margin-inline: calc(50% - 50vw);
|
|
72
|
+
border-radius: 0;
|
|
73
|
+
padding-inline: max(clamp(1.25rem, 5vw, 4rem), calc((100vw - 72rem) / 2 + 2rem));
|
|
74
|
+
}
|
|
63
75
|
/* Parallax: pin the image to the viewport so it slides under the content.
|
|
64
76
|
Only on fine pointers with motion allowed — `fixed` is ignored/janky on
|
|
65
77
|
touch (iOS) and unwelcome under reduced-motion. */
|
|
@@ -45,9 +45,11 @@ const fmt = (date: Date | string) => {
|
|
|
45
45
|
: { iso: d.toISOString().slice(0, 10), label: d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }) };
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
// "New"
|
|
49
|
-
//
|
|
50
|
-
//
|
|
48
|
+
// The "New" badge marks only the single most-recent post, and only if it was
|
|
49
|
+
// published within `newWithinDays` of build time — so a site that posts often
|
|
50
|
+
// doesn't end up flagging every row (which would make the badge meaningless).
|
|
51
|
+
// Recomputed each build (a site rebuilds on publish, so it stays fresh); isRecent
|
|
52
|
+
// lives in the pure posts module so it's unit-tested independently.
|
|
51
53
|
const nowMs = Date.now();
|
|
52
54
|
const isNew = (date: Date | string) => isRecent(date, newWithinDays, nowMs);
|
|
53
55
|
---
|
|
@@ -64,7 +66,7 @@ const isNew = (date: Date | string) => isRecent(date, newWithinDays, nowMs);
|
|
|
64
66
|
<p class="b-latest-posts__empty">No posts yet.</p>
|
|
65
67
|
) : layout === 'list' ? (
|
|
66
68
|
<ul class="b-latest-posts__list">
|
|
67
|
-
{posts.map((p) => {
|
|
69
|
+
{posts.map((p, i) => {
|
|
68
70
|
const { iso, label } = fmt(p.data.date);
|
|
69
71
|
const href = `/posts/${p.slug}`;
|
|
70
72
|
const cats = (p.data.tags ?? []).slice(0, 2);
|
|
@@ -82,7 +84,7 @@ const isNew = (date: Date | string) => isRecent(date, newWithinDays, nowMs);
|
|
|
82
84
|
<h3 class="b-post-row__title"><a href={href}>{p.data.title}</a></h3>
|
|
83
85
|
</div>
|
|
84
86
|
<div class="b-post-row__aside">
|
|
85
|
-
{isNew(p.data.date) && <span class="b-post-row__new">New</span>}
|
|
87
|
+
{i === 0 && isNew(p.data.date) && <span class="b-post-row__new">New</span>}
|
|
86
88
|
{label && <time class="b-post-row__date" datetime={iso}>{label}</time>}
|
|
87
89
|
<span class="b-post-row__chevron" aria-hidden="true">›</span>
|
|
88
90
|
</div>
|
package/content/blocks.ts
CHANGED
|
@@ -268,6 +268,10 @@ export const bannerBlock = z.object({
|
|
|
268
268
|
overlay: z.number().min(0).max(100).default(50),
|
|
269
269
|
align: z.enum(['start', 'center', 'end']).default('start'),
|
|
270
270
|
parallax: z.boolean().default(true),
|
|
271
|
+
/** Break the band out to the full viewport width (edge-to-edge, no rounded
|
|
272
|
+
* corners); the card stays aligned to the page's content column. Great for a
|
|
273
|
+
* homepage hero. Off by default (the band sits inside the content column). */
|
|
274
|
+
fullBleed: z.boolean().default(false),
|
|
271
275
|
});
|
|
272
276
|
|
|
273
277
|
/** Stats — a band of headline numbers (value + label + optional note), over an
|
|
@@ -396,8 +400,9 @@ export const latestPostsBlock = z.object({
|
|
|
396
400
|
/** `grid` = full image cards; `list` = a compact miniature index (thumbnail +
|
|
397
401
|
* category + date per row) that stays short even with many posts. */
|
|
398
402
|
layout: z.enum(['grid', 'list']).default('grid'),
|
|
399
|
-
/** In the `list` layout,
|
|
400
|
-
*
|
|
403
|
+
/** In the `list` layout, badge the most-recent post as "New" when it was
|
|
404
|
+
* published within this many days (0 disables it). Only the newest row is ever
|
|
405
|
+
* flagged, so a frequently-posting site doesn't mark every row. Grid ignores it. */
|
|
401
406
|
newWithinDays: z.number().int().min(0).max(365).default(14),
|
|
402
407
|
/** "View all" target + label (the posts index by default). */
|
|
403
408
|
viewAllHref: z.string().default('/posts'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ferst-core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ferst Core — the shared, brand-agnostic client-site system: Astro components, layouts, content schemas, the layered config resolver, and a neutral styling architecture that every client overrides.",
|
|
6
6
|
"license": "BUSL-1.1",
|