diff --git a/app/(marketing)/learn/page.tsx b/app/(marketing)/learn/page.tsx index 04a63dd..045a95f 100644 --- a/app/(marketing)/learn/page.tsx +++ b/app/(marketing)/learn/page.tsx @@ -32,6 +32,20 @@ export const metadata: Metadata = routeMetadata("/learn", { const CASE_STUDY_TILE_RADIUS_CLASS = "rounded-[23.093px]"; +/** Figma card grid: 1 column below 640, 2 from 640, 3 from 1024, 4 from 1440. */ +const ARTICLE_GRID_CLASS = [ + "flex flex-col gap-[var(--space-050)]", + "md:flex-row md:flex-wrap md:justify-center md:gap-[var(--space-400)]", + "md:px-[var(--space-1200)] md:py-[var(--space-600)]", + "lg:px-[var(--space-1800)] lg:pt-[var(--space-800)] lg:pb-[var(--space-1800)]", + "[&>*]:min-w-0 [&>*]:w-full", + "md:[&>*]:w-[calc((100%-var(--space-400))/2)]", + "lg:[&>*]:w-[calc((100%-2*var(--space-400))/3)]", + "xl:[&>*]:w-[calc((100%-3*var(--space-400))/4)]", +].join(" "); + +const GUIDE_TAG_SRC = "/content/blog/how-communityrule-works-tag.svg"; + const CASE_STUDY_LINK_CLASS = [ CASE_STUDY_TILE_RADIUS_CLASS, "block shrink-0 cursor-pointer outline-none transition-[opacity,transform] duration-200", @@ -163,14 +177,14 @@ export default function LearnPage() {
{articlePosts.map((post) => ( diff --git a/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.container.tsx b/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.container.tsx index 0391318..f6626ea 100644 --- a/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.container.tsx +++ b/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.container.tsx @@ -24,6 +24,7 @@ const ContentThumbnailTemplateContainer = memo( showByline = true, showLeadingImage = true, leadingImageAlt, + leadingImageSrc, }) => { const variant = variantProp; const sizing = sizingProp; @@ -56,7 +57,7 @@ const ContentThumbnailTemplateContainer = memo( }; // For "responsive", emit both orientations so the source can - // swap at smd without a second card in the DOM. + // swap at md (640px) without a second card in the DOM. const backgroundImage = variant === "responsive" ? getBackgroundImage(post, "horizontal") @@ -78,6 +79,7 @@ const ContentThumbnailTemplateContainer = memo( showByline={showByline} showLeadingImage={showLeadingImage} leadingImageAlt={leadingImageAlt} + leadingImageSrc={leadingImageSrc} /> ); }, diff --git a/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.types.ts b/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.types.ts index b98b52c..5958fa3 100644 --- a/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.types.ts +++ b/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.types.ts @@ -11,8 +11,8 @@ export interface ContentThumbnailTemplateProps { post: BlogPost; className?: string; /** - * vertical | horizontal — single layout. responsive — horizontal at . + * vertical | horizontal — single layout. responsive — horizontal below md + * (640px), vertical from md up (Learn grid); one card, swapped via . */ variant?: ContentThumbnailTemplateVariantValue; /** @@ -28,6 +28,8 @@ export interface ContentThumbnailTemplateProps { showLeadingImage?: boolean; /** Alt for the tag mark. Empty when the mark repeats the title. */ leadingImageAlt?: string; + /** Tag mark source. Falls back to the catalog tag for the post slug. */ + leadingImageSrc?: string; } export interface ContentThumbnailTemplateViewProps { @@ -42,4 +44,5 @@ export interface ContentThumbnailTemplateViewProps { showByline: boolean; showLeadingImage: boolean; leadingImageAlt?: string; + leadingImageSrc?: string; } diff --git a/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.view.tsx b/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.view.tsx index 866574c..3eb15b3 100644 --- a/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.view.tsx +++ b/app/components/content/ContentThumbnailTemplate/ContentThumbnailTemplate.view.tsx @@ -14,18 +14,19 @@ function ContentThumbnailTemplateView({ showByline, showLeadingImage, leadingImageAlt, + leadingImageSrc, }: ContentThumbnailTemplateViewProps) { if (variant === "responsive") { - // Single card; swaps the orientation-specific image at smd - // (530px), aspect-ratio and content positioning switch via Tailwind. + // Single card; swaps to the vertical image at md (640px), + // which is where Learn's grid goes from one landscape card to two. return ( -
+
{backgroundImageSmd ? ( ) : null} @@ -37,13 +38,14 @@ function ContentThumbnailTemplateView({ />
-
+
@@ -72,6 +74,7 @@ function ContentThumbnailTemplateView({ showByline={showByline} showLeadingImage={showLeadingImage} leadingImageAlt={leadingImageAlt} + leadingImageSrc={leadingImageSrc} />
@@ -97,6 +100,7 @@ function ContentThumbnailTemplateView({ showByline={showByline} showLeadingImage={showLeadingImage} leadingImageAlt={leadingImageAlt} + leadingImageSrc={leadingImageSrc} />
@@ -124,6 +128,7 @@ function ContentThumbnailTemplateView({ showByline={showByline} showLeadingImage={showLeadingImage} leadingImageAlt={leadingImageAlt} + leadingImageSrc={leadingImageSrc} /> @@ -149,6 +154,7 @@ function ContentThumbnailTemplateView({ showByline={showByline} showLeadingImage={showLeadingImage} leadingImageAlt={leadingImageAlt} + leadingImageSrc={leadingImageSrc} /> diff --git a/public/content/blog/how-communityrule-works-horizontal.svg b/public/content/blog/how-communityrule-works-horizontal.svg index d76a004..d841739 100644 --- a/public/content/blog/how-communityrule-works-horizontal.svg +++ b/public/content/blog/how-communityrule-works-horizontal.svg @@ -1,3 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/content/blog/how-communityrule-works-tag.svg b/public/content/blog/how-communityrule-works-tag.svg new file mode 100644 index 0000000..b8fd584 --- /dev/null +++ b/public/content/blog/how-communityrule-works-tag.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/content/blog/how-communityrule-works-vertical.svg b/public/content/blog/how-communityrule-works-vertical.svg index a3dc954..8d24983 100644 --- a/public/content/blog/how-communityrule-works-vertical.svg +++ b/public/content/blog/how-communityrule-works-vertical.svg @@ -1,3 +1,15 @@ + + + + + + + + + + + + diff --git a/tests/pages/learn.test.tsx b/tests/pages/learn.test.tsx index 4e23ce8..7a5ad9c 100644 --- a/tests/pages/learn.test.tsx +++ b/tests/pages/learn.test.tsx @@ -119,7 +119,14 @@ describe("LearnPage", () => { render(); const grid = screen.getByTestId("learn-article-grid"); - expect(grid).toHaveClass("smd:justify-center", "smd:flex-wrap"); + expect(grid).toHaveClass("md:justify-center", "md:flex-wrap"); + expect(grid).toHaveClass( + "lg:[&>*]:w-[calc((100%-2*var(--space-400))/3)]", + ); + expect(grid).toHaveClass( + "xl:[&>*]:w-[calc((100%-3*var(--space-400))/4)]", + ); + expect(grid.className).not.toContain("/5)]"); const links = within(grid).getAllByRole("link"); expect(links).toHaveLength(mockPosts.length + 1); @@ -136,7 +143,7 @@ describe("LearnPage", () => { expect(within(grid).queryByText("April 15, 2025")).not.toBeInTheDocument(); const sources = grid.querySelectorAll( - "picture source[media='(min-width: 530px)']", + "picture source[media='(min-width: 640px)']", ); expect(sources.length).toBe(mockPosts.length + 1);