+ {/* Main Content — Figma Content page Template (19003:23305) article body instances */}
+
+
diff --git a/app/(marketing)/blog/blog.css b/app/(marketing)/blog/blog.css
index 60e5fad..e5c7fd3 100644
--- a/app/(marketing)/blog/blog.css
+++ b/app/(marketing)/blog/blog.css
@@ -1,4 +1,8 @@
/* Blog post body styling with semantic spacing */
+.post-body > :first-child {
+ margin-block-start: 0;
+}
+
.post-body p {
/* Scales with font size - uses logical properties for better writing mode support */
margin-block: 1em;
diff --git a/app/components/content/ContentContainer/ContentContainer.container.tsx b/app/components/content/ContentContainer/ContentContainer.container.tsx
index a60ca4a..cd119f7 100644
--- a/app/components/content/ContentContainer/ContentContainer.container.tsx
+++ b/app/components/content/ContentContainer/ContentContainer.container.tsx
@@ -6,9 +6,8 @@
*/
import { memo } from "react";
import {
- getAssetPath,
- ASSETS,
contentBlogTagPath,
+ contentCatalogSlugForFallback,
CONTENT_CATALOG_SLUG_ORDER,
} from "../../../../lib/assetUtils";
import ContentContainerView from "./ContentContainer.view";
@@ -36,26 +35,14 @@ const ContentContainerContainer = memo(
: "text-[var(--color-content-inverse-brand-royal)]";
const getIconImage = (slug: string): string => {
- const icons = [
- getAssetPath(ASSETS.ICON_1),
- getAssetPath(ASSETS.ICON_2),
- getAssetPath(ASSETS.ICON_3),
- ];
+ const resolvedSlug =
+ CONTENT_CATALOG_SLUG_ORDER.indexOf(
+ slug as (typeof CONTENT_CATALOG_SLUG_ORDER)[number],
+ ) >= 0
+ ? slug
+ : contentCatalogSlugForFallback(slug);
- if (!slug) return icons[0];
-
- const index = CONTENT_CATALOG_SLUG_ORDER.indexOf(
- slug as (typeof CONTENT_CATALOG_SLUG_ORDER)[number],
- );
- if (index >= 0) {
- return contentBlogTagPath(slug);
- }
-
- const fallbackIndex =
- Math.abs(
- slug.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0),
- ) % icons.length;
- return icons[fallbackIndex];
+ return contentBlogTagPath(resolvedSlug);
};
const iconImage = leadingImageSrc ?? getIconImage(post.slug);
diff --git a/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.container.tsx b/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.container.tsx
index 673412a..648b1c3 100644
--- a/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.container.tsx
+++ b/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.container.tsx
@@ -1,11 +1,16 @@
"use client";
/**
- * Figma: "Components" / ContentThumnailTemplate (19614-14838, 19041-13415).
- * Vertical 260×390 (19060-15787); horizontal 320×225.5 (19041-13550).
+ * Figma: "Components" / Thumbnail (19428:22574).
+ * Vertical 260×390; horizontal 320×225.5; per-article backgrounds in public/content/blog/.
*/
import { memo } from "react";
-import { getAssetPath, ASSETS } from "../../../../lib/assetUtils";
+import {
+ contentBlogHorizontalPath,
+ contentBlogVerticalPath,
+ contentCatalogSlugForFallback,
+ CONTENT_CATALOG_SLUG_ORDER,
+} from "../../../../lib/assetUtils";
import ContentThumbnailTemplateView from "./ContentThumbnailTemplate.view";
import type { ContentThumbnailTemplateProps } from "./ContentThumbnailTemplate.types";
@@ -23,7 +28,6 @@ const ContentThumbnailTemplateContainer = memo(
post: ContentThumbnailTemplateProps["post"],
variant: "vertical" | "horizontal",
): string => {
- // Check if post has thumbnail images defined in frontmatter
if (post.frontmatter?.thumbnail) {
const imageName =
variant === "vertical"
@@ -31,18 +35,21 @@ const ContentThumbnailTemplateContainer = memo(
: post.frontmatter.thumbnail.horizontal;
if (imageName) {
- // Return path to image in public/content/blog directory
return `/content/blog/${imageName}`;
}
}
- // Fallback to default images if no thumbnail specified
- const fallbackImages: Record = {
- vertical: getAssetPath(ASSETS.VERTICAL_1),
- horizontal: getAssetPath(ASSETS.HORIZONTAL_1),
- };
+ const slug = post.slug;
+ const resolvedSlug =
+ CONTENT_CATALOG_SLUG_ORDER.indexOf(
+ slug as (typeof CONTENT_CATALOG_SLUG_ORDER)[number],
+ ) >= 0
+ ? slug
+ : contentCatalogSlugForFallback(slug);
- return fallbackImages[variant] || fallbackImages.vertical;
+ return variant === "vertical"
+ ? contentBlogVerticalPath(resolvedSlug)
+ : contentBlogHorizontalPath(resolvedSlug);
};
const backgroundImage = getBackgroundImage(post, variant);
diff --git a/app/components/sections/ContentBanner/ContentBanner.container.tsx b/app/components/sections/ContentBanner/ContentBanner.container.tsx
index 381bf0e..bb05a7f 100644
--- a/app/components/sections/ContentBanner/ContentBanner.container.tsx
+++ b/app/components/sections/ContentBanner/ContentBanner.container.tsx
@@ -1,12 +1,17 @@
"use client";
import { memo } from "react";
-import { getAssetPath } from "../../../../lib/assetUtils";
+import {
+ getAssetPath,
+ contentBlogHorizontalPath,
+ contentBlogSectionPath,
+ CONTENT_CATALOG_SLUG_ORDER,
+} from "../../../../lib/assetUtils";
import type { BlogPost } from "../../../../lib/content";
import ContentBannerView from "./ContentBanner.view";
import type { ContentBannerProps } from "./ContentBanner.types";
-/** Figma: Section / ContentBanner — article (blog) and guide (content page template 22078:791901). */
+/** Figma: Content page Template (19003:23305) — article ContentBanner per breakpoint. */
const ContentBannerContainer = memo(
({
post,
@@ -18,27 +23,42 @@ const ContentBannerContainer = memo(
}) => {
const variant = variantProp;
- const getBackgroundImage = (blogPost: BlogPost): string => {
+ const resolveHorizontalImage = (blogPost: BlogPost): string => {
if (blogPost.frontmatter?.thumbnail?.horizontal) {
return `/content/blog/${blogPost.frontmatter.thumbnail.horizontal}`;
}
+
+ if (
+ CONTENT_CATALOG_SLUG_ORDER.includes(
+ blogPost.slug as (typeof CONTENT_CATALOG_SLUG_ORDER)[number],
+ )
+ ) {
+ return contentBlogHorizontalPath(blogPost.slug);
+ }
+
return getAssetPath("assets/Content_Banner.svg");
};
- const getBannerImageMd = (blogPost: BlogPost): string => {
+ const resolveSectionImage = (blogPost: BlogPost): string => {
if (blogPost.frontmatter?.banner?.horizontal) {
return `/content/blog/${blogPost.frontmatter.banner.horizontal}`;
}
- if (blogPost.frontmatter?.thumbnail?.horizontal) {
- return `/content/blog/${blogPost.frontmatter.thumbnail.horizontal}`;
+
+ if (
+ CONTENT_CATALOG_SLUG_ORDER.includes(
+ blogPost.slug as (typeof CONTENT_CATALOG_SLUG_ORDER)[number],
+ )
+ ) {
+ return contentBlogSectionPath(blogPost.slug);
}
- return getAssetPath("assets/Content_Banner_2.svg");
+
+ return resolveHorizontalImage(blogPost);
};
- const backgroundImageSm =
- variant === "article" ? getBackgroundImage(post) : undefined;
- const backgroundImageMd =
- variant === "article" ? getBannerImageMd(post) : undefined;
+ const backgroundImageHorizontal =
+ variant === "article" ? resolveHorizontalImage(post) : undefined;
+ const backgroundImageSection =
+ variant === "article" ? resolveSectionImage(post) : undefined;
return (
(
post={post}
leadingImageSrc={leadingImageSrc}
leadingImageAlt={leadingImageAlt}
- backgroundImageSm={backgroundImageSm}
- backgroundImageMd={backgroundImageMd}
+ backgroundImageHorizontal={backgroundImageHorizontal}
+ backgroundImageSection={backgroundImageSection}
rulePreview={rulePreview}
contentTone={contentTone}
/>
diff --git a/app/components/sections/ContentBanner/ContentBanner.types.ts b/app/components/sections/ContentBanner/ContentBanner.types.ts
index eeff36e..5161ada 100644
--- a/app/components/sections/ContentBanner/ContentBanner.types.ts
+++ b/app/components/sections/ContentBanner/ContentBanner.types.ts
@@ -35,8 +35,10 @@ export interface ContentBannerViewProps {
post: BlogPost;
leadingImageSrc?: string;
leadingImageAlt?: string;
- backgroundImageSm?: string;
- backgroundImageMd?: string;
+ /** Article variant: horizontal thumbnail below lg (`320×225.5`). */
+ backgroundImageHorizontal?: string;
+ /** Article variant: section banner at md+ (`1920×672`, Figma Section orientation). */
+ backgroundImageSection?: string;
rulePreview?: ContentBannerRulePreview;
contentTone?: ContentContainerToneValue;
}
diff --git a/app/components/sections/ContentBanner/ContentBanner.view.tsx b/app/components/sections/ContentBanner/ContentBanner.view.tsx
index e564ee8..646f30e 100644
--- a/app/components/sections/ContentBanner/ContentBanner.view.tsx
+++ b/app/components/sections/ContentBanner/ContentBanner.view.tsx
@@ -68,44 +68,71 @@ function ContentBannerGuideView({
);
}
+/**
+ * Figma: Content page Template (19003:23305) — ContentBanner article instances.
+ * Horizontal thumbnail below md; Section SVG (1920×672) at md+.
+ */
function ContentBannerArticleView({
post,
leadingImageSrc,
leadingImageAlt,
- backgroundImageSm,
- backgroundImageMd,
+ backgroundImageHorizontal,
+ backgroundImageSection,
}: ContentBannerViewProps) {
- if (!backgroundImageSm || !backgroundImageMd) {
+ if (!backgroundImageHorizontal || !backgroundImageSection) {
return null;
}
return (
-
acc + c.charCodeAt(0), 0),
+ ) % CONTENT_CATALOG_SLUG_ORDER.length;
+ return CONTENT_CATALOG_SLUG_ORDER[index];
+}
+
/**
* Asset paths for common components
*/
@@ -101,17 +132,6 @@ export const ASSETS = {
BLUESKY_LOGO: "assets/Bluesky_Logo.svg",
GITLAB_ICON: "assets/GitLab_Icon.png",
- // Content thumbnails
- VERTICAL_1: "assets/Content_Thumbnail/Vertical_1.svg",
- VERTICAL_2: "assets/Content_Thumbnail/Vertical_2.svg",
- VERTICAL_3: "assets/Content_Thumbnail/Vertical_3.svg",
- HORIZONTAL_1: "assets/Content_Thumbnail/Horizontal_1.svg",
- HORIZONTAL_2: "assets/Content_Thumbnail/Horizontal_2.svg",
- HORIZONTAL_3: "assets/Content_Thumbnail/Horizontal_3.svg",
- ICON_1: "assets/Content_Thumbnail/Icon_1.svg",
- ICON_2: "assets/Content_Thumbnail/Icon_2.svg",
- ICON_3: "assets/Content_Thumbnail/Icon_3.svg",
-
// Content page decorative shapes
CONTENT_SHAPE_1: "assets/Content_Shape_1.svg",
CONTENT_SHAPE_2: "assets/Content_Shape_2.svg",
diff --git a/public/assets/Content_Thumbnail/Horizontal_1.svg b/public/assets/Content_Thumbnail/Horizontal_1.svg
deleted file mode 100644
index 838dafd..0000000
--- a/public/assets/Content_Thumbnail/Horizontal_1.svg
+++ /dev/null
@@ -1,31 +0,0 @@
-
diff --git a/public/assets/Content_Thumbnail/Horizontal_2.svg b/public/assets/Content_Thumbnail/Horizontal_2.svg
deleted file mode 100644
index ff6736d..0000000
--- a/public/assets/Content_Thumbnail/Horizontal_2.svg
+++ /dev/null
@@ -1,28 +0,0 @@
-
diff --git a/public/assets/Content_Thumbnail/Horizontal_3.svg b/public/assets/Content_Thumbnail/Horizontal_3.svg
deleted file mode 100644
index d1a81fa..0000000
--- a/public/assets/Content_Thumbnail/Horizontal_3.svg
+++ /dev/null
@@ -1,25 +0,0 @@
-
diff --git a/public/assets/Content_Thumbnail/Icon_1.svg b/public/assets/Content_Thumbnail/Icon_1.svg
deleted file mode 100644
index b8fd584..0000000
--- a/public/assets/Content_Thumbnail/Icon_1.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/public/assets/Content_Thumbnail/Icon_2.svg b/public/assets/Content_Thumbnail/Icon_2.svg
deleted file mode 100644
index 873505c..0000000
--- a/public/assets/Content_Thumbnail/Icon_2.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-
diff --git a/public/assets/Content_Thumbnail/Icon_3.svg b/public/assets/Content_Thumbnail/Icon_3.svg
deleted file mode 100644
index 0bf598d..0000000
--- a/public/assets/Content_Thumbnail/Icon_3.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/public/assets/Content_Thumbnail/Vertical_1.svg b/public/assets/Content_Thumbnail/Vertical_1.svg
deleted file mode 100644
index ff5f25f..0000000
--- a/public/assets/Content_Thumbnail/Vertical_1.svg
+++ /dev/null
@@ -1,31 +0,0 @@
-
diff --git a/public/assets/Content_Thumbnail/Vertical_2.svg b/public/assets/Content_Thumbnail/Vertical_2.svg
deleted file mode 100644
index 0b9aa36..0000000
--- a/public/assets/Content_Thumbnail/Vertical_2.svg
+++ /dev/null
@@ -1,34 +0,0 @@
-
diff --git a/public/assets/Content_Thumbnail/Vertical_3.svg b/public/assets/Content_Thumbnail/Vertical_3.svg
deleted file mode 100644
index bc3df93..0000000
--- a/public/assets/Content_Thumbnail/Vertical_3.svg
+++ /dev/null
@@ -1,35 +0,0 @@
-
diff --git a/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-horizontal.svg b/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-horizontal.svg
index b2148f5..6a806b0 100644
--- a/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-horizontal.svg
+++ b/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-horizontal.svg
@@ -1,31 +1,29 @@
diff --git a/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-section.svg b/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-section.svg
new file mode 100644
index 0000000..9462c92
--- /dev/null
+++ b/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-section.svg
@@ -0,0 +1,32 @@
+
diff --git a/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-tag.svg b/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-tag.svg
index 873505c..6cc21ef 100644
--- a/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-tag.svg
+++ b/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-tag.svg
@@ -1,18 +1,3 @@
-
+
+
+ aria-hidden
+ className="pointer-events-none absolute inset-x-0 top-0 -bottom-[var(--spacing-scale-024)] sm:-bottom-[var(--spacing-scale-032)] md:hidden"
+ data-name="ContentBannerBackgroundHorizontal"
+ >
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
+ aria-hidden
+ className="pointer-events-none absolute inset-x-0 top-0 -bottom-[var(--spacing-scale-032)] hidden md:block"
+ data-name="ContentBannerBackgroundSection"
+ >
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+