diff --git a/app/(marketing)/blog/[slug]/page.tsx b/app/(marketing)/blog/[slug]/page.tsx index c7dc218..2b1ca9f 100644 --- a/app/(marketing)/blog/[slug]/page.tsx +++ b/app/(marketing)/blog/[slug]/page.tsx @@ -1,6 +1,7 @@ import { notFound } from "next/navigation"; import type { Metadata } from "next"; import dynamic from "next/dynamic"; +import type { BlogPost } from "../../../../lib/content"; import { getBlogPostBySlug, getAllBlogPosts as getAllPosts, @@ -201,7 +202,7 @@ export default async function BlogPostPage({ params }: PageProps) { />
{/* Content Banner */} @@ -242,10 +243,16 @@ export default async function BlogPostPage({ params }: PageProps) { />
- {/* Main Content */} -
- {/* Article Content */} -
+ {/* 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 ( -
+
+ 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 */} + +
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 @@ - - - - - - - - - - - - - - - - - + + diff --git a/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-vertical.svg b/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-vertical.svg index 374f144..ccad3d8 100644 --- a/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-vertical.svg +++ b/public/content/blog/avoiding-burnout-sustainability-in-the-ruins-vertical.svg @@ -1,31 +1,27 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - diff --git a/public/content/blog/digital-mediation-and-the-death-of-nuance-horizontal.svg b/public/content/blog/digital-mediation-and-the-death-of-nuance-horizontal.svg index 838dafd..4959815 100644 --- a/public/content/blog/digital-mediation-and-the-death-of-nuance-horizontal.svg +++ b/public/content/blog/digital-mediation-and-the-death-of-nuance-horizontal.svg @@ -1,31 +1,64 @@ - - - - - - - - - - - - - - - + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + diff --git a/public/content/blog/digital-mediation-and-the-death-of-nuance-section.svg b/public/content/blog/digital-mediation-and-the-death-of-nuance-section.svg new file mode 100644 index 0000000..cb6ac0b --- /dev/null +++ b/public/content/blog/digital-mediation-and-the-death-of-nuance-section.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/content/blog/digital-mediation-and-the-death-of-nuance-tag.svg b/public/content/blog/digital-mediation-and-the-death-of-nuance-tag.svg index b8fd584..a33917f 100644 --- a/public/content/blog/digital-mediation-and-the-death-of-nuance-tag.svg +++ b/public/content/blog/digital-mediation-and-the-death-of-nuance-tag.svg @@ -1,14 +1,3 @@ - - - - - - - - - - - - - + + diff --git a/public/content/blog/digital-mediation-and-the-death-of-nuance-vertical.svg b/public/content/blog/digital-mediation-and-the-death-of-nuance-vertical.svg index ff5f25f..cdf55cf 100644 --- a/public/content/blog/digital-mediation-and-the-death-of-nuance-vertical.svg +++ b/public/content/blog/digital-mediation-and-the-death-of-nuance-vertical.svg @@ -1,31 +1,74 @@ - - - - - - - - - + + + + - - + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + diff --git a/public/content/blog/how-chaos-concentrates-control-horizontal.svg b/public/content/blog/how-chaos-concentrates-control-horizontal.svg index 90757e9..a15869f 100644 --- a/public/content/blog/how-chaos-concentrates-control-horizontal.svg +++ b/public/content/blog/how-chaos-concentrates-control-horizontal.svg @@ -1,31 +1,24 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - - - diff --git a/public/content/blog/how-chaos-concentrates-control-section.svg b/public/content/blog/how-chaos-concentrates-control-section.svg new file mode 100644 index 0000000..c31d945 --- /dev/null +++ b/public/content/blog/how-chaos-concentrates-control-section.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/content/blog/how-chaos-concentrates-control-tag.svg b/public/content/blog/how-chaos-concentrates-control-tag.svg index 0bf598d..fbe3d79 100644 --- a/public/content/blog/how-chaos-concentrates-control-tag.svg +++ b/public/content/blog/how-chaos-concentrates-control-tag.svg @@ -1,16 +1,3 @@ - - - - - - - - - - - - - - - + + diff --git a/public/content/blog/how-chaos-concentrates-control-vertical.svg b/public/content/blog/how-chaos-concentrates-control-vertical.svg index 7757e91..c49ba70 100644 --- a/public/content/blog/how-chaos-concentrates-control-vertical.svg +++ b/public/content/blog/how-chaos-concentrates-control-vertical.svg @@ -1,31 +1,40 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - diff --git a/public/content/blog/integrating-new-members-without-dilution-horizontal.svg b/public/content/blog/integrating-new-members-without-dilution-horizontal.svg index 2f0152f..ff50dae 100644 --- a/public/content/blog/integrating-new-members-without-dilution-horizontal.svg +++ b/public/content/blog/integrating-new-members-without-dilution-horizontal.svg @@ -1,31 +1,25 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + - - - diff --git a/public/content/blog/integrating-new-members-without-dilution-section.svg b/public/content/blog/integrating-new-members-without-dilution-section.svg new file mode 100644 index 0000000..da9a232 --- /dev/null +++ b/public/content/blog/integrating-new-members-without-dilution-section.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/content/blog/integrating-new-members-without-dilution-tag.svg b/public/content/blog/integrating-new-members-without-dilution-tag.svg index b8fd584..6482a7e 100644 --- a/public/content/blog/integrating-new-members-without-dilution-tag.svg +++ b/public/content/blog/integrating-new-members-without-dilution-tag.svg @@ -1,14 +1,3 @@ - - - - - - - - - - - - - + + diff --git a/public/content/blog/integrating-new-members-without-dilution-vertical.svg b/public/content/blog/integrating-new-members-without-dilution-vertical.svg index 3bb6d8e..9869c02 100644 --- a/public/content/blog/integrating-new-members-without-dilution-vertical.svg +++ b/public/content/blog/integrating-new-members-without-dilution-vertical.svg @@ -1,31 +1,27 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + diff --git a/public/content/blog/knowledge-management-and-institutional-amnesia-horizontal.svg b/public/content/blog/knowledge-management-and-institutional-amnesia-horizontal.svg index 44f1935..28c027c 100644 --- a/public/content/blog/knowledge-management-and-institutional-amnesia-horizontal.svg +++ b/public/content/blog/knowledge-management-and-institutional-amnesia-horizontal.svg @@ -1,31 +1,31 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - + - - + + diff --git a/public/content/blog/knowledge-management-and-institutional-amnesia-section.svg b/public/content/blog/knowledge-management-and-institutional-amnesia-section.svg new file mode 100644 index 0000000..c4c46ef --- /dev/null +++ b/public/content/blog/knowledge-management-and-institutional-amnesia-section.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/content/blog/knowledge-management-and-institutional-amnesia-tag.svg b/public/content/blog/knowledge-management-and-institutional-amnesia-tag.svg index 873505c..d0c0a7e 100644 --- a/public/content/blog/knowledge-management-and-institutional-amnesia-tag.svg +++ b/public/content/blog/knowledge-management-and-institutional-amnesia-tag.svg @@ -1,18 +1,3 @@ - - - - - - - - - - - - - - - - - + + diff --git a/public/content/blog/knowledge-management-and-institutional-amnesia-vertical.svg b/public/content/blog/knowledge-management-and-institutional-amnesia-vertical.svg index 5e7e991..5ae8097 100644 --- a/public/content/blog/knowledge-management-and-institutional-amnesia-vertical.svg +++ b/public/content/blog/knowledge-management-and-institutional-amnesia-vertical.svg @@ -1,31 +1,32 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + diff --git a/public/content/blog/making-decisions-without-hierarchy-horizontal.svg b/public/content/blog/making-decisions-without-hierarchy-horizontal.svg index eda1836..55dc5dc 100644 --- a/public/content/blog/making-decisions-without-hierarchy-horizontal.svg +++ b/public/content/blog/making-decisions-without-hierarchy-horizontal.svg @@ -1,31 +1,25 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + - - - diff --git a/public/content/blog/making-decisions-without-hierarchy-section.svg b/public/content/blog/making-decisions-without-hierarchy-section.svg new file mode 100644 index 0000000..7b2757e --- /dev/null +++ b/public/content/blog/making-decisions-without-hierarchy-section.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/content/blog/making-decisions-without-hierarchy-vertical.svg b/public/content/blog/making-decisions-without-hierarchy-vertical.svg index e477b9c..bc3df93 100644 --- a/public/content/blog/making-decisions-without-hierarchy-vertical.svg +++ b/public/content/blog/making-decisions-without-hierarchy-vertical.svg @@ -1,31 +1,35 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + diff --git a/public/content/blog/operational-security-mutual-aid-horizontal.svg b/public/content/blog/operational-security-mutual-aid-horizontal.svg index d1a81fa..ff6736d 100644 --- a/public/content/blog/operational-security-mutual-aid-horizontal.svg +++ b/public/content/blog/operational-security-mutual-aid-horizontal.svg @@ -1,24 +1,27 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + diff --git a/public/content/blog/operational-security-mutual-aid-section.svg b/public/content/blog/operational-security-mutual-aid-section.svg new file mode 100644 index 0000000..fe2ff4e --- /dev/null +++ b/public/content/blog/operational-security-mutual-aid-section.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/content/blog/operational-security-mutual-aid-vertical.svg b/public/content/blog/operational-security-mutual-aid-vertical.svg index bc3df93..0b9aa36 100644 --- a/public/content/blog/operational-security-mutual-aid-vertical.svg +++ b/public/content/blog/operational-security-mutual-aid-vertical.svg @@ -1,34 +1,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/public/content/blog/resolving-active-conflicts-section.svg b/public/content/blog/resolving-active-conflicts-section.svg new file mode 100644 index 0000000..2c5ffd1 --- /dev/null +++ b/public/content/blog/resolving-active-conflicts-section.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/components/ContentBanner.test.tsx b/tests/components/ContentBanner.test.tsx index d8c7b67..fb86b28 100644 --- a/tests/components/ContentBanner.test.tsx +++ b/tests/components/ContentBanner.test.tsx @@ -62,6 +62,61 @@ describe("ContentBanner", () => { expect(screen.getByText("Test description")).toBeInTheDocument(); }); + it("renders article banner with horizontal thumbnail below md and section SVG at md+", () => { + const { container } = render( + , + ); + + const banner = container.querySelector('[data-node-id="19189:9053"]'); + expect(banner).toBeInTheDocument(); + expect(banner).toHaveClass("min-h-[275px]", "md:min-h-[224px]", "xl:min-h-[504px]"); + + const horizontalBackground = container.querySelector( + '[data-name="ContentBannerBackgroundHorizontal"]', + ); + expect(horizontalBackground).toHaveClass( + "absolute", + "inset-x-0", + "top-0", + "md:hidden", + ); + expect(horizontalBackground?.querySelector("img")).toHaveAttribute( + "src", + "/content/blog/resolving-active-conflicts-horizontal.svg", + ); + + const sectionBackground = container.querySelector( + '[data-name="ContentBannerBackgroundSection"]', + ); + expect(sectionBackground).toHaveClass( + "absolute", + "inset-x-0", + "top-0", + "hidden", + "md:block", + ); + expect(sectionBackground?.querySelector("img")).toHaveAttribute( + "src", + "/content/blog/resolving-active-conflicts-section.svg", + ); + + const copyColumn = container.querySelector('[data-node-id="19189:9010"]'); + expect(copyColumn).toHaveClass("md:max-w-[280px]", "lg:max-w-[365px]", "xl:max-w-[623px]"); + expect(copyColumn).not.toHaveClass("h-[160px]"); + }); + it("renders useCase variant rule preview as link when href is set", () => { const { container } = render( { .getByText(/This is the article content/) .closest("div.post-body"); expect(contentDiv).toHaveClass("post-body"); - expect(contentDiv).toHaveClass("-mt-[var(--spacing-scale-048)]"); expect(contentDiv).toHaveClass( "text-[var(--color-content-inverse-primary)]", ); expect(contentDiv).toHaveClass("text-[16px]"); expect(contentDiv).toHaveClass("leading-[24px]"); + + const article = contentDiv?.closest("article"); + expect(article).toHaveClass("p-[var(--spacing-scale-024)]"); + expect(article).toHaveClass("sm:py-[var(--spacing-scale-032)]"); + expect(article).not.toHaveClass("-mt-[var(--spacing-scale-048)]"); }); it("applies responsive text sizing", async () => { @@ -280,7 +284,6 @@ describe("BlogPostPage", () => { const contentDiv = screen .getByText(/This is the article content/) .closest("div.post-body"); - expect(contentDiv).toHaveClass("sm:mx-auto"); expect(contentDiv).toHaveClass("sm:max-w-[390px]"); expect(contentDiv).toHaveClass("md:max-w-[472px]"); expect(contentDiv).toHaveClass("lg:max-w-[700px]"); diff --git a/tests/unit/ContentContainer.test.jsx b/tests/unit/ContentContainer.test.jsx index 5fe4aae..e2b79f1 100644 --- a/tests/unit/ContentContainer.test.jsx +++ b/tests/unit/ContentContainer.test.jsx @@ -4,18 +4,25 @@ import ContentContainer from "../../app/components/content/ContentContainer"; // Mock asset utils vi.mock("../../lib/assetUtils", () => ({ - getAssetPath: vi.fn((asset) => `/assets/${asset}`), contentBlogTagPath: vi.fn((slug) => `/content/blog/${slug}-tag.svg`), + contentCatalogSlugForFallback: vi.fn((slug) => { + const catalog = [ + "resolving-active-conflicts", + "operational-security-mutual-aid", + "making-decisions-without-hierarchy", + ]; + if (!slug) return catalog[0]; + const index = + Math.abs( + slug.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0), + ) % catalog.length; + return catalog[index]; + }), CONTENT_CATALOG_SLUG_ORDER: [ "resolving-active-conflicts", "operational-security-mutual-aid", "making-decisions-without-hierarchy", ], - ASSETS: { - ICON_1: "Icon_1.svg", - ICON_2: "Icon_2.svg", - ICON_3: "Icon_3.svg", - }, })); // Mock blog post data @@ -52,7 +59,10 @@ describe("ContentContainer", () => { const icon = screen.getByAltText("Icon for Test Article Title"); expect(icon).toBeInTheDocument(); - expect(icon).toHaveAttribute("src", "/assets/Icon_1.svg"); + expect(icon).toHaveAttribute( + "src", + "/content/blog/resolving-active-conflicts-tag.svg", + ); expect(icon).toHaveClass("w-[60px]", "h-[30px]", "object-contain"); }); @@ -159,7 +169,10 @@ describe("ContentContainer", () => { const { rerender } = render(); let icon = screen.getByAltText("Icon for Test Article Title"); - expect(icon).toHaveAttribute("src", "/assets/Icon_1.svg"); + expect(icon).toHaveAttribute( + "src", + "/content/blog/resolving-active-conflicts-tag.svg", + ); const post2 = { ...mockPost, slug: "operational-security-mutual-aid" }; rerender();