Match the marketing homepage and About page to the latest Figma: black hero CTA, spaces in word-split headings, redesigned feature grid, and cited About statistics.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilallo
2026-09-09 17:40:11 -06:00
co-authored by Cursor
parent 2d351987cf
commit 40225e2845
32 changed files with 362 additions and 219 deletions
+2 -9
View File
@@ -36,11 +36,6 @@ export default function AboutPage() {
const headerSegments = asArray<AboutHeaderSegment>(page.aboutHeader.segments);
const statsItems = asArray<StatItem>(page.stats.items);
const statsAsOf =
typeof page.stats.asOf === "string"
? page.stats.asOf
: String(page.stats.asOf ?? "");
const faqItems = asArray<FaqAccordionItem>(page.faq.items);
const tripleColumns = asArray<TripleTextBlockColumn>(page.tripleTextBlock.columns);
@@ -57,10 +52,8 @@ export default function AboutPage() {
titlePrefix={page.stats.titlePrefix}
titleEmphasis={page.stats.titleEmphasis}
titleSuffix={page.stats.titleSuffix}
items={statsItems.map((item) => ({
...item,
asOf: statsAsOf,
}))}
asOfPrefix={page.stats.asOfPrefix}
items={statsItems}
/>
<TripleTextBlock columns={tripleColumns} />
<Book
+3
View File
@@ -4,6 +4,9 @@ export interface StatProps {
value: string;
label: string;
asOf?: string;
asOfPrefix?: string;
sourceHref?: string;
sourceName?: string;
shapeVariant?: StatShapeVariant;
className?: string;
}
+22 -1
View File
@@ -11,9 +11,14 @@ function StatView({
value,
label,
asOf,
asOfPrefix,
sourceHref,
sourceName,
shapeVariant,
className = "",
}: StatViewProps) {
const sourcedYear = Boolean(asOfPrefix || sourceHref);
return (
<article
className={`relative flex h-auto min-h-[182px] w-full flex-col items-start justify-between rounded-[var(--radius-measures-radius-xlarge,20px)] bg-[var(--color-surface-invert-primary,white)] px-[var(--spacing-scale-024)] py-[var(--spacing-scale-032)] sm:h-[170px] sm:min-h-0 sm:p-[var(--spacing-scale-024)] ${className}`.trim()}
@@ -34,7 +39,23 @@ function StatView({
</div>
{asOf ? (
<p className="w-full text-xx-small-paragraph text-[var(--color-content-invert-tertiary,#2d2d2d)]">
{asOf}
{sourcedYear ? (
<>
{asOfPrefix ? <span>{`${asOfPrefix} `}</span> : null}
{sourceHref ? (
<a href={sourceHref} className="underline">
{asOf}
{sourceName ? (
<span className="sr-only">{` (${sourceName})`}</span>
) : null}
</a>
) : (
<span className="underline">{asOf}</span>
)}
</>
) : (
asOf
)}
</p>
) : null}
</article>
@@ -1,11 +1,11 @@
"use client";
/**
* Figma: "Section / Feature-Grid" (18847:22410)
* Figma: "Section / Feature-Grid" (18632:10668)
*/
import { memo, useMemo } from "react";
import { getAssetPath, featurePanelLayout, featurePanelPath } from "../../../../lib/assetUtils";
import { featureIconPath, getAssetPath } from "../../../../lib/assetUtils";
import { useTranslation } from "../../../contexts/MessagesContext";
import FeatureGridView from "./FeatureGrid.view";
import type { FeatureGridProps, Feature } from "./FeatureGrid.types";
@@ -17,52 +17,40 @@ const FeatureGridContainer = memo<FeatureGridProps>(
const features: Feature[] = useMemo(
() => [
{
backgroundColor: "bg-[var(--color-surface-invert-brand-royal)]",
labelLine1: t(
"pages.home.featureGrid.features.decisionMaking.labelLine1",
backgroundColor: "bg-[var(--color-surface-invert-brand-lavender)]",
title: t("pages.home.featureGrid.features.decisionMaking.title"),
description: t(
"pages.home.featureGrid.features.decisionMaking.description",
),
labelLine2: t(
"pages.home.featureGrid.features.decisionMaking.labelLine2",
),
panelContent: getAssetPath(featurePanelPath("support")),
...featurePanelLayout("support"),
ariaLabel: t("featureGrid.features.decisionMaking.ariaLabel"),
descriptionClassName: "text-[#3a3273]",
iconSrc: getAssetPath(featureIconPath("git-branch")),
},
{
backgroundColor: "bg-[var(--color-surface-invert-brand-lime)]",
labelLine1: t(
"pages.home.featureGrid.features.valuesAlignment.labelLine1",
title: t("pages.home.featureGrid.features.valuesAlignment.title"),
description: t(
"pages.home.featureGrid.features.valuesAlignment.description",
),
labelLine2: t(
"pages.home.featureGrid.features.valuesAlignment.labelLine2",
),
panelContent: getAssetPath(featurePanelPath("exercises")),
...featurePanelLayout("exercises"),
ariaLabel: t("featureGrid.features.valuesAlignment.ariaLabel"),
descriptionClassName: "text-[#424f1a]",
iconSrc: getAssetPath(featureIconPath("arrow-left-right")),
},
{
backgroundColor: "bg-[var(--color-surface-invert-brand-rust)]",
labelLine1: t(
"pages.home.featureGrid.features.membershipGuidance.labelLine1",
title: t("pages.home.featureGrid.features.membershipGuidance.title"),
description: t(
"pages.home.featureGrid.features.membershipGuidance.description",
),
labelLine2: t(
"pages.home.featureGrid.features.membershipGuidance.labelLine2",
),
panelContent: getAssetPath(featurePanelPath("guidance")),
...featurePanelLayout("guidance"),
ariaLabel: t("featureGrid.features.membershipGuidance.ariaLabel"),
descriptionClassName: "text-[#5e2e23]",
iconSrc: getAssetPath(featureIconPath("door-open")),
},
{
backgroundColor: "bg-[var(--color-surface-invert-brand-teal)]",
labelLine1: t(
"pages.home.featureGrid.features.conflictResolution.labelLine1",
title: t("pages.home.featureGrid.features.conflictResolution.title"),
description: t(
"pages.home.featureGrid.features.conflictResolution.description",
),
labelLine2: t(
"pages.home.featureGrid.features.conflictResolution.labelLine2",
),
panelContent: getAssetPath(featurePanelPath("tools")),
...featurePanelLayout("tools"),
ariaLabel: t("featureGrid.features.conflictResolution.ariaLabel"),
descriptionClassName: "text-[#1f4d47]",
iconSrc: getAssetPath(featureIconPath("message-square-share")),
},
],
[t],
@@ -6,13 +6,10 @@ export interface FeatureGridProps {
export interface Feature {
backgroundColor: string;
labelLine1: string;
labelLine2: string;
panelContent: string;
panelWidth: number;
panelHeight: number;
panelImageClassName?: string;
ariaLabel: string;
title: string;
description: string;
descriptionClassName: string;
iconSrc: string;
}
export interface FeatureGridViewProps extends FeatureGridProps {
@@ -2,10 +2,9 @@
import { useTranslation } from "../../../contexts/MessagesContext";
import ContentLockup from "../../type/ContentLockup";
import Mini from "../../cards/Mini";
import type { FeatureGridViewProps } from "./FeatureGrid.types";
/** Figma **Section / Feature-Grid** [18847:22410](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=18847-22410&m=dev). */
/** Figma **Section / Feature-Grid** [18632:10668](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=18632-10668&m=dev). */
function FeatureGridView({
title,
subtitle,
@@ -20,16 +19,16 @@ function FeatureGridView({
return (
<section
className={`p-0 lg:p-[var(--spacing-scale-064)] ${className}`}
className={`p-0 md:px-[var(--spacing-scale-024)] lg:p-[var(--spacing-scale-064)] ${className}`}
aria-labelledby={labelledBy}
aria-label={labelledBy ? undefined : ariaLabel}
>
<div
data-figma-node="18847-22410"
className="rounded-[var(--measures-radius-500,20px)] bg-[var(--color-surface-default-secondary)] px-[var(--spacing-scale-020)] py-[var(--spacing-scale-032)] md:px-[var(--spacing-scale-048)] md:pb-[var(--spacing-scale-048)] md:pt-[var(--spacing-scale-076)] lg:pb-[var(--spacing-scale-076)]"
data-figma-node="18632-10668"
className="rounded-none bg-[var(--color-surface-default-secondary)] px-[var(--spacing-scale-016)] py-[var(--spacing-scale-032)] md:rounded-[var(--radius-measures-radius-large)] md:p-[var(--spacing-scale-048)] lg:p-[var(--spacing-scale-064)]"
>
<div className="mx-auto w-full gap-[var(--spacing-scale-048)] [container-type:inline-size] lg:flex lg:items-start lg:gap-[var(--spacing-scale-048)]">
<div className="lg:min-w-0 lg:shrink">
<div className="mx-auto flex w-full flex-col gap-[var(--spacing-scale-040)] md:gap-[var(--spacing-scale-048)] lg:flex-row lg:items-center lg:gap-[var(--spacing-scale-064)]">
<div className="lg:min-w-0 lg:flex-1">
<ContentLockup
title={title}
subtitle={subtitle}
@@ -40,20 +39,34 @@ function FeatureGridView({
/>
</div>
<div className="mt-[var(--spacing-scale-048)] grid grid-cols-2 grid-rows-[repeat(2,minmax(0,1fr))] gap-x-[12px] gap-y-[12px] max-md:min-h-[384px] md:grid-cols-4 md:grid-rows-1 md:min-h-0 lg:mt-0 lg:shrink-0 lg:flex-grow">
{features.map((feature, index) => (
<Mini
key={index}
backgroundColor={feature.backgroundColor}
labelLine1={feature.labelLine1}
labelLine2={feature.labelLine2}
panelContent={feature.panelContent}
panelWidth={feature.panelWidth}
panelHeight={feature.panelHeight}
panelImageClassName={feature.panelImageClassName}
ariaLabel={feature.ariaLabel}
featureGridShell
/>
<div className="grid w-full grid-cols-1 gap-[var(--spacing-scale-008)] md:grid-cols-2 md:gap-[var(--spacing-scale-016)] lg:flex-1">
{features.map((feature) => (
<div
key={feature.title}
className={`flex flex-col gap-[var(--spacing-scale-016)] p-[var(--spacing-scale-024)] ${feature.backgroundColor} rounded-[var(--radius-200)] md:rounded-[var(--radius-400)] lg:rounded-[var(--radius-measures-radius-large)]`}
>
<div className="size-[40px] shrink-0 overflow-clip">
{/* eslint-disable-next-line @next/next/no-img-element -- decorative Figma glyph */}
<img
src={feature.iconSrc}
alt=""
width={40}
height={40}
className="size-full object-contain"
role="presentation"
/>
</div>
<div className="flex w-full flex-col gap-[var(--spacing-scale-008)]">
<p className="font-semibold text-[17px] leading-[22px] text-[var(--color-content-inverse-primary)]">
{feature.title}
</p>
<p
className={`text-small-paragraph ${feature.descriptionClassName}`}
>
{feature.description}
</p>
</div>
</div>
))}
</div>
</div>
@@ -4,6 +4,9 @@ export interface StatItem {
value: string;
label: string;
asOf?: string;
asOfPrefix?: string;
sourceHref?: string;
sourceName?: string;
shapeVariant?: StatShapeVariant;
}
@@ -11,6 +14,7 @@ export interface StatsProps {
titlePrefix?: string;
titleEmphasis?: string;
titleSuffix?: string;
asOfPrefix?: string;
items: StatItem[];
className?: string;
}
+6 -1
View File
@@ -21,6 +21,7 @@ function StatsView({
titlePrefix,
titleEmphasis,
titleSuffix,
asOfPrefix,
items,
headingId,
className = "",
@@ -88,7 +89,11 @@ function StatsView({
return (
<li key={`${item.value}-${index}`} className={staggerClass}>
<Stat {...item} className={heightClass} />
<Stat
{...item}
asOfPrefix={item.asOfPrefix ?? asOfPrefix}
className={heightClass}
/>
</li>
);
})}
@@ -25,16 +25,27 @@ function AboutHeaderView({
variant="about"
alignment="left"
titleId={titleId}
titleContent={segments.map((segment, index) => {
if (segment.type === "word") {
return (
<span key={`${segment.text}-${index}`} className="whitespace-nowrap">
{segment.text}
titleContent={segments.flatMap((segment, index) => {
const spaceBefore =
segment.type === "word" && index > 0 ? (
<span key={`space-${index}`} className="sr-only">
{" "}
</span>
);
) : null;
if (segment.type === "word") {
return [
spaceBefore,
<span
key={`${segment.text}-${index}`}
className="whitespace-nowrap"
>
{segment.text}
</span>,
];
}
return (
return [
<span
key={`${segment.icon}-${index}`}
className={
@@ -50,8 +61,8 @@ function AboutHeaderView({
className="size-full object-contain"
role="presentation"
/>
</span>
);
</span>,
];
})}
/>
</section>
@@ -84,8 +84,13 @@ function ContentLockupView({
)}
</div>
{/* Subtitle */}
{subtitle ? <h2 className={styles.subtitle}>{subtitle}</h2> : null}
{subtitle ? (
variant === "feature" ? (
<p className={styles.subtitle}>{subtitle}</p>
) : (
<h2 className={styles.subtitle}>{subtitle}</h2>
)
) : null}
</div>
{/* Description */}
@@ -110,7 +115,7 @@ function ContentLockupView({
<div className="block md:hidden">
<Button
buttonType="filled"
palette={variant === "hero" ? "default" : "inverse"}
palette="inverse"
size="small"
href={ctaHref}
>
@@ -121,7 +126,7 @@ function ContentLockupView({
<div className="hidden md:block xl:hidden">
<Button
buttonType="filled"
palette={variant === "hero" ? "default" : "inverse"}
palette="inverse"
size="large"
className={buttonClassName}
href={ctaHref}
@@ -133,7 +138,7 @@ function ContentLockupView({
<div className="hidden xl:block">
<Button
buttonType="filled"
palette={variant === "hero" ? "default" : "inverse"}
palette="inverse"
size="xlarge"
href={ctaHref}
>
@@ -79,8 +79,8 @@ const SectionHeader = memo<SectionHeaderProps>(
</span>
{useStackedDesktop ? (
<span className={splitFromMd ? "hidden md:block" : "hidden lg:block"}>
<span className="block">{stackedDesktopLines[0]}</span>
<span className="block">{stackedDesktopLines[1]}</span>
<span className="block">{stackedDesktopLines[0]} </span>
<span className="block">{stackedDesktopLines[1]} </span>
<span className="block">{stackedDesktopLines[2]}</span>
</span>
) : (